You need to sign in or sign up before continuing.

newrelic_rpm 9.18.0 → 9.20.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 (99) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +56 -0
  3. data/lib/new_relic/agent/agent.rb +2 -0
  4. data/lib/new_relic/agent/configuration/default_source.rb +112 -85
  5. data/lib/new_relic/agent/configuration/manager.rb +5 -2
  6. data/lib/new_relic/agent/configuration/yaml_source.rb +2 -2
  7. data/lib/new_relic/agent/database.rb +1 -1
  8. data/lib/new_relic/agent/database_adapter.rb +1 -1
  9. data/lib/new_relic/agent/datastores/redis.rb +1 -1
  10. data/lib/new_relic/agent/distributed_tracing/cross_app_tracing.rb +1 -1
  11. data/lib/new_relic/agent/distributed_tracing.rb +2 -0
  12. data/lib/new_relic/agent/external.rb +2 -0
  13. data/lib/new_relic/agent/instrumentation/action_dispatch.rb +1 -1
  14. data/lib/new_relic/agent/instrumentation/action_dispatch_subscriber.rb +1 -1
  15. data/lib/new_relic/agent/instrumentation/action_mailbox.rb +1 -1
  16. data/lib/new_relic/agent/instrumentation/action_mailer.rb +1 -1
  17. data/lib/new_relic/agent/instrumentation/active_job.rb +1 -1
  18. data/lib/new_relic/agent/instrumentation/active_job_subscriber.rb +6 -2
  19. data/lib/new_relic/agent/instrumentation/active_record.rb +6 -4
  20. data/lib/new_relic/agent/instrumentation/active_record_helper.rb +2 -2
  21. data/lib/new_relic/agent/instrumentation/active_record_notifications.rb +11 -9
  22. data/lib/new_relic/agent/instrumentation/active_record_prepend.rb +2 -2
  23. data/lib/new_relic/agent/instrumentation/async_http.rb +1 -1
  24. data/lib/new_relic/agent/instrumentation/aws_sdk_kinesis/instrumentation.rb +1 -1
  25. data/lib/new_relic/agent/instrumentation/concurrent_ruby.rb +1 -1
  26. data/lib/new_relic/agent/instrumentation/controller_instrumentation.rb +4 -0
  27. data/lib/new_relic/agent/instrumentation/curb.rb +1 -1
  28. data/lib/new_relic/agent/instrumentation/elasticsearch/chain.rb +1 -2
  29. data/lib/new_relic/agent/instrumentation/elasticsearch.rb +1 -1
  30. data/lib/new_relic/agent/instrumentation/ethon.rb +1 -1
  31. data/lib/new_relic/agent/instrumentation/excon.rb +1 -1
  32. data/lib/new_relic/agent/instrumentation/fiber/chain.rb +1 -1
  33. data/lib/new_relic/agent/instrumentation/fiber/prepend.rb +1 -1
  34. data/lib/new_relic/agent/instrumentation/httpclient.rb +1 -4
  35. data/lib/new_relic/agent/instrumentation/httpx/instrumentation.rb +1 -1
  36. data/lib/new_relic/agent/instrumentation/httpx.rb +1 -1
  37. data/lib/new_relic/agent/instrumentation/logstasher.rb +1 -1
  38. data/lib/new_relic/agent/instrumentation/memcache/dalli.rb +1 -1
  39. data/lib/new_relic/agent/instrumentation/memcache/helper.rb +2 -2
  40. data/lib/new_relic/agent/instrumentation/memcache/instrumentation.rb +1 -1
  41. data/lib/new_relic/agent/instrumentation/memcache/prepend.rb +1 -1
  42. data/lib/new_relic/agent/instrumentation/mongodb_command_subscriber.rb +1 -1
  43. data/lib/new_relic/agent/instrumentation/net_http.rb +2 -1
  44. data/lib/new_relic/agent/instrumentation/rake.rb +1 -1
  45. data/lib/new_relic/agent/instrumentation/rdkafka/chain.rb +2 -2
  46. data/lib/new_relic/agent/instrumentation/rdkafka/prepend.rb +2 -2
  47. data/lib/new_relic/agent/instrumentation/redis/constants.rb +2 -2
  48. data/lib/new_relic/agent/instrumentation/resque.rb +2 -2
  49. data/lib/new_relic/agent/instrumentation/roda.rb +1 -1
  50. data/lib/new_relic/agent/instrumentation/ruby_kafka/prepend.rb +1 -1
  51. data/lib/new_relic/agent/instrumentation/ruby_openai.rb +2 -2
  52. data/lib/new_relic/agent/instrumentation/sidekiq/extensions/delayed_class.rb +1 -1
  53. data/lib/new_relic/agent/instrumentation/stripe.rb +1 -1
  54. data/lib/new_relic/agent/instrumentation/typhoeus/instrumentation.rb +2 -2
  55. data/lib/new_relic/agent/llm/chat_completion_summary.rb +1 -1
  56. data/lib/new_relic/agent/llm/embedding.rb +1 -1
  57. data/lib/new_relic/agent/local_log_decorator.rb +1 -1
  58. data/lib/new_relic/agent/logging.rb +1 -1
  59. data/lib/new_relic/agent/messaging.rb +5 -0
  60. data/lib/new_relic/agent/method_tracer.rb +3 -0
  61. data/lib/new_relic/agent/monitors/inbound_request_monitor.rb +1 -1
  62. data/lib/new_relic/agent/monitors/synthetics_monitor.rb +1 -1
  63. data/lib/new_relic/agent/new_relic_service/json_marshaller.rb +2 -2
  64. data/lib/new_relic/agent/opentelemetry/context/propagation/trace_propagator.rb +66 -0
  65. data/lib/new_relic/agent/opentelemetry/context/propagation.rb +15 -0
  66. data/lib/new_relic/agent/opentelemetry/context.rb +13 -0
  67. data/lib/new_relic/agent/opentelemetry/trace/span.rb +31 -0
  68. data/lib/new_relic/agent/opentelemetry/trace/tracer.rb +129 -0
  69. data/lib/new_relic/agent/opentelemetry/trace/tracer_provider.rb +18 -0
  70. data/lib/new_relic/agent/opentelemetry/trace.rb +15 -0
  71. data/lib/new_relic/agent/opentelemetry/transaction_patch.rb +69 -0
  72. data/lib/new_relic/agent/opentelemetry_bridge.rb +32 -0
  73. data/lib/new_relic/agent/parameter_filtering.rb +1 -1
  74. data/lib/new_relic/agent/samplers/cpu_sampler.rb +1 -1
  75. data/lib/new_relic/agent/samplers/memory_sampler.rb +1 -1
  76. data/lib/new_relic/agent/span_event_primitive.rb +8 -1
  77. data/lib/new_relic/agent/tracer.rb +1 -1
  78. data/lib/new_relic/agent/transaction/abstract_segment.rb +2 -1
  79. data/lib/new_relic/agent/transaction/datastore_segment.rb +1 -1
  80. data/lib/new_relic/agent/transaction/distributed_tracer.rb +3 -3
  81. data/lib/new_relic/agent/transaction/message_broker_segment.rb +1 -1
  82. data/lib/new_relic/agent/transaction/request_attributes.rb +1 -6
  83. data/lib/new_relic/agent/transaction/trace_context.rb +33 -4
  84. data/lib/new_relic/agent/transaction_time_aggregator.rb +1 -1
  85. data/lib/new_relic/agent/utilization/ecs.rb +22 -0
  86. data/lib/new_relic/agent/utilization/ecs_v4.rb +22 -0
  87. data/lib/new_relic/agent/utilization_data.rb +25 -0
  88. data/lib/new_relic/agent/vm/c_ruby_vm.rb +3 -3
  89. data/lib/new_relic/agent.rb +28 -0
  90. data/lib/new_relic/constants.rb +1 -0
  91. data/lib/new_relic/control/instrumentation.rb +1 -1
  92. data/lib/new_relic/dependency_detection.rb +0 -1
  93. data/lib/new_relic/helper.rb +7 -0
  94. data/lib/new_relic/version.rb +1 -1
  95. data/lib/sequel/extensions/new_relic_instrumentation.rb +1 -1
  96. data/lib/tasks/helpers/newrelicyml.rb +2 -0
  97. data/newrelic.yml +25 -15
  98. data/newrelic_rpm.gemspec +1 -1
  99. metadata +16 -6
@@ -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
@@ -0,0 +1,66 @@
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 OpenTelemetry
8
+ module Context
9
+ module Propagation
10
+ class TracePropagator
11
+ # The carrier is the object carrying the headers
12
+ # The context argument is a no-op, as the OpenTelemetry context is not used
13
+ # The setter argument is a no-op, added for consistency with the OpenTelemetry API
14
+ def inject(carrier, context: ::OpenTelemetry::Context.current, setter: nil)
15
+ # TODO: determine if we need to update this method to take Context into account
16
+ NewRelic::Agent::DistributedTracing.insert_distributed_trace_headers(carrier)
17
+ end
18
+
19
+ # The return value for this method should be an instance of the
20
+ # OpenTelemetry Context class. The return value of
21
+ # #accept_distributed_trace_headers is a transaction, so we cannot
22
+ # use it to extract the context.
23
+ def extract(carrier, context: ::OpenTelemetry::Context.current, getter: ::OpenTelemetry::Context::Propagation.text_map_getter)
24
+ carrier_format = determine_format(getter)
25
+ trace_context = NewRelic::Agent::DistributedTracing::TraceContext.parse(
26
+ carrier: carrier,
27
+ format: carrier_format,
28
+ trace_state_entry_key: Transaction::TraceContext::AccountHelpers.trace_state_entry_key
29
+ )
30
+ tp = trace_context.trace_parent
31
+ span_context = ::OpenTelemetry::Trace::SpanContext.new(
32
+ trace_id: tp['trace_id'],
33
+ span_id: tp['parent_id'],
34
+ trace_flags: tp['trace_flags'],
35
+ tracestate: trace_context.trace_state_payload,
36
+ remote: true
37
+ )
38
+ span = ::OpenTelemetry::Trace.non_recording_span(span_context)
39
+
40
+ ::OpenTelemetry::Trace.context_with_span(span, parent_context: context)
41
+ rescue StandardError => e
42
+ NewRelic::Agent.logger.error("Unable to extract context: #{e.message}")
43
+ context
44
+ end
45
+
46
+ private
47
+
48
+ # The getter is the way OpenTelemetry handles Rack vs. non-Rack
49
+ # formats. Rather than using their parser, get the class info we
50
+ # need to do things the New Relic way
51
+ def determine_format(getter)
52
+ case getter
53
+ when ::OpenTelemetry::Context::Propagation::RackEnvGetter
54
+ FORMAT_RACK
55
+ when defined?(::OpenTelemetry::Common) && ::OpenTelemetry::Common::Propagation::RackEnvGetter
56
+ FORMAT_RACK
57
+ else
58
+ FORMAT_NON_RACK
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,15 @@
1
+ # This file is distributed under New Relic's license terms.
2
+ # See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
3
+ # frozen_string_literal: true
4
+
5
+ module NewRelic
6
+ module Agent
7
+ module OpenTelemetry
8
+ module Context
9
+ module Propagation
10
+ require_relative 'propagation/trace_propagator'
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,13 @@
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 OpenTelemetry
8
+ module Context
9
+ require_relative 'context/propagation'
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,31 @@
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 OpenTelemetry
8
+ module Trace
9
+ class Span < ::OpenTelemetry::Trace::Span
10
+ attr_accessor :finishable
11
+
12
+ def finish(end_timestamp: nil)
13
+ finishable&.finish
14
+ end
15
+
16
+ def set_attribute(key, value)
17
+ NewRelic::Agent.add_custom_span_attributes(key => value)
18
+ end
19
+
20
+ def add_attributes(attributes)
21
+ NewRelic::Agent.add_custom_span_attributes(attributes)
22
+ end
23
+
24
+ def record_exception(exception, attributes: nil)
25
+ NewRelic::Agent.notice_error(exception, attributes: attributes)
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,129 @@
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 OpenTelemetry
8
+ module Trace
9
+ class Tracer < ::OpenTelemetry::Trace::Tracer
10
+ def initialize(name = nil, version = nil)
11
+ @name = name || ''
12
+ @version = version || ''
13
+ end
14
+
15
+ def start_span(name, with_parent: nil, attributes: nil, links: nil, start_timestamp: nil, kind: nil)
16
+ parent_otel_context = ::OpenTelemetry::Trace.current_span(with_parent).context
17
+
18
+ finishable = if can_start_transaction?(parent_otel_context)
19
+ return if internal_span_kind_with_invalid_parent?(kind, parent_otel_context)
20
+
21
+ nr_item = NewRelic::Agent::Tracer.start_transaction_or_segment(name: name, category: :otel)
22
+ add_remote_context_to_txn(nr_item, parent_otel_context)
23
+ nr_item
24
+ else
25
+ NewRelic::Agent::Tracer.start_segment(name: name)
26
+ end
27
+
28
+ otel_span = get_otel_span_from_finishable(finishable)
29
+ otel_span.finishable = finishable
30
+ add_remote_context_to_otel_span(otel_span, parent_otel_context)
31
+ otel_span.add_attributes(attributes) if attributes
32
+ otel_span
33
+ end
34
+
35
+ def in_span(name, attributes: nil, links: nil, start_timestamp: nil, kind: nil)
36
+ span = start_span(name, attributes: attributes, links: links, start_timestamp: start_timestamp, kind: kind)
37
+ begin
38
+ yield
39
+ rescue => e
40
+ # TODO: Update for segment errors if finishable is a segment
41
+ NewRelic::Agent.notice_error(e)
42
+ raise
43
+ end
44
+ ensure
45
+ span&.finish
46
+ end
47
+
48
+ private
49
+
50
+ def get_otel_span_from_finishable(finishable)
51
+ case finishable
52
+ when NewRelic::Agent::Transaction
53
+ finishable.segments.first.instance_variable_get(:@otel_span)
54
+ when NewRelic::Agent::Transaction::Segment
55
+ finishable.instance_variable_get(:@otel_span)
56
+ else
57
+ NewRelic::Agent.logger.warn('Tracer#get_otel_span_from_finishable failed to get span from finishable - finishable is not a transaction or segment')
58
+ nil
59
+ end
60
+ end
61
+
62
+ def can_start_transaction?(parent_otel_context)
63
+ parent_otel_context.remote? || !parent_otel_context.valid?
64
+ end
65
+
66
+ def internal_span_kind_with_invalid_parent?(kind, parent_otel_context)
67
+ !parent_otel_context.valid? && kind == :internal
68
+ end
69
+
70
+ def transaction_and_remote_parent?(txn, parent_otel_context)
71
+ txn.is_a?(NewRelic::Agent::Transaction) && parent_otel_context.remote?
72
+ end
73
+
74
+ def add_remote_context_to_txn(txn, parent_otel_context)
75
+ return unless transaction_and_remote_parent?(txn, parent_otel_context)
76
+
77
+ txn.trace_id = parent_otel_context.trace_id
78
+ txn.parent_span_id = parent_otel_context.span_id
79
+
80
+ set_tracestate(txn.distributed_tracer, parent_otel_context)
81
+ end
82
+
83
+ def set_tracestate(distributed_tracer, otel_context)
84
+ case otel_context.tracestate
85
+ when ::OpenTelemetry::Trace::Tracestate
86
+ set_otel_trace_state(distributed_tracer, otel_context)
87
+ when NewRelic::Agent::TraceContextPayload
88
+ set_nr_trace_state(distributed_tracer, otel_context)
89
+ end
90
+ end
91
+
92
+ def set_nr_trace_state(distributed_tracer, otel_context)
93
+ distributed_tracer.instance_variable_set(:@trace_state_payload, otel_context.tracestate)
94
+ distributed_tracer.parent_transaction_id = distributed_tracer.trace_state_payload.transaction_id
95
+ distributed_tracer.determine_sampling_decision(otel_context.tracestate, otel_context.trace_flags)
96
+ end
97
+
98
+ def set_otel_trace_state(distributed_tracer, otel_context)
99
+ nr_entry = otel_context.tracestate.value(Transaction::TraceContext::AccountHelpers.trace_state_entry_key)
100
+ return unless nr_entry
101
+
102
+ nr_payload = NewRelic::Agent::TraceContextPayload.from_s(nr_entry)
103
+ distributed_tracer.instance_variable_set(:@trace_state_payload, nr_payload)
104
+ distributed_tracer.parent_transaction_id = distributed_tracer.trace_state_payload.transaction_id
105
+ trace_flags = parse_trace_flags(otel_context.trace_flags)
106
+ distributed_tracer.determine_sampling_decision(nr_payload, trace_flags)
107
+ end
108
+
109
+ def parse_trace_flags(trace_flags)
110
+ case trace_flags
111
+ when String
112
+ trace_flags
113
+ when Integer
114
+ trace_flags.to_s
115
+ when ::OpenTelemetry::Trace::TraceFlags
116
+ trace_flags.sampled? ? '01' : '00'
117
+ end
118
+ end
119
+
120
+ def add_remote_context_to_otel_span(otel_span, parent_otel_context)
121
+ return unless transaction_and_remote_parent?(otel_span.finishable, parent_otel_context)
122
+
123
+ otel_span.context.instance_variable_set(:@trace_id, otel_span.finishable.trace_id)
124
+ end
125
+ end
126
+ end
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,18 @@
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 OpenTelemetry
8
+ module Trace
9
+ class TracerProvider < ::OpenTelemetry::Trace::TracerProvider
10
+ # TODO: Add a registration mechanism for tracers like exists in the SDK
11
+ def tracer(name = nil, version = nil)
12
+ @tracer ||= Tracer.new(name, version)
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,15 @@
1
+ # This file is distributed under New Relic's license terms.
2
+ # See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
3
+ # frozen_string_literal: true
4
+
5
+ module NewRelic
6
+ module Agent
7
+ module OpenTelemetry
8
+ module Trace
9
+ require_relative 'trace/tracer_provider'
10
+ require_relative 'trace/tracer'
11
+ require_relative 'trace/span'
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,69 @@
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 OpenTelemetry
8
+ module TransactionPatch
9
+ attr_accessor :opentelemetry_context
10
+
11
+ def initialize(_category, _options)
12
+ @opentelemetry_context = {}
13
+ super
14
+ end
15
+
16
+ def set_current_segment(new_segment)
17
+ @current_segment_lock.synchronize do
18
+ unless opentelemetry_context.empty?
19
+ ::OpenTelemetry::Context.detach(opentelemetry_context[otel_current_span_key])
20
+ end
21
+
22
+ span = find_or_create_span(new_segment)
23
+ ctx = ::OpenTelemetry::Context.current.set_value(otel_current_span_key, span)
24
+ token = ::OpenTelemetry::Context.attach(ctx)
25
+
26
+ opentelemetry_context[otel_current_span_key] = token
27
+ end
28
+
29
+ super
30
+ end
31
+
32
+ def remove_current_segment_by_thread_id(id)
33
+ # make sure the context is fully detached when the transaction ends
34
+ @current_segment_lock.synchronize do
35
+ ::OpenTelemetry::Context.detach(opentelemetry_context[otel_current_span_key])
36
+ opentelemetry_context.delete(id)
37
+ end
38
+
39
+ super
40
+ end
41
+
42
+ private
43
+
44
+ def find_or_create_span(segment)
45
+ if segment.instance_variable_defined?(:@otel_span)
46
+ segment.instance_variable_get(:@otel_span)
47
+ else
48
+ span = Trace::Span.new(span_context: span_context_from_segment(segment))
49
+ segment.instance_variable_set(:@otel_span, span)
50
+ span
51
+ end
52
+ end
53
+
54
+ def span_context_from_segment(segment)
55
+ ::OpenTelemetry::Trace::SpanContext.new(
56
+ trace_id: segment.transaction.trace_id,
57
+ span_id: segment.guid,
58
+ remote: false
59
+ )
60
+ end
61
+
62
+ def otel_current_span_key
63
+ # CURRENT_SPAN_KEY is a private constant
64
+ ::OpenTelemetry::Trace.const_get(:CURRENT_SPAN_KEY)
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,32 @@
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
+ class OpenTelemetryBridge
8
+ def initialize
9
+ # no-op without OpenTelemetry API & config
10
+ return unless defined?(OpenTelemetry) &&
11
+ NewRelic::Agent.config[:'opentelemetry_bridge.enabled']
12
+
13
+ OpenTelemetryBridge.install
14
+ end
15
+
16
+ private
17
+
18
+ def self.install
19
+ require 'opentelemetry' # requires the opentelemetry-api gem
20
+ require_relative 'opentelemetry/trace'
21
+ require_relative 'opentelemetry/transaction_patch'
22
+ require_relative 'opentelemetry/context'
23
+
24
+ # TODO: Add a warning if SDK gem is installed
25
+
26
+ ::OpenTelemetry.tracer_provider = OpenTelemetry::Trace::TracerProvider.new
27
+ Transaction.prepend(OpenTelemetry::TransactionPatch)
28
+ ::OpenTelemetry.propagation = OpenTelemetry::Context::Propagation::TracePropagator.new
29
+ end
30
+ end
31
+ end
32
+ end
@@ -9,7 +9,7 @@ module NewRelic
9
9
 
10
10
  ACTION_DISPATCH_PARAMETER_FILTER ||= 'action_dispatch.parameter_filter'.freeze
11
11
 
12
- if defined?(Rails) && Gem::Version.new(::Rails::VERSION::STRING) >= Gem::Version.new('5.0.0')
12
+ if defined?(Rails) && NewRelic::Helper.version_satisfied?(::Rails::VERSION::STRING, '>=', '5.0.0')
13
13
  Rails.application.config.to_prepare do
14
14
  RAILS_FILTER_CLASS ||= if defined?(ActiveSupport::ParameterFilter)
15
15
  ActiveSupport::ParameterFilter
@@ -42,7 +42,7 @@ module NewRelic
42
42
  # Process.times on JRuby < 1.7.0 reports wall clock elapsed time,
43
43
  # not actual cpu time used, so this sampler can only be used on JRuby >= 1.7.0.
44
44
  if defined?(JRuby)
45
- return JRUBY_VERSION >= '1.7.0'
45
+ return NewRelic::Helper.version_satisfied?(JRUBY_VERSION, '>=', '1.7.0')
46
46
  end
47
47
 
48
48
  true
@@ -50,7 +50,7 @@ module NewRelic
50
50
  begin
51
51
  NewRelic::Helper.run_command('uname -s').downcase
52
52
  rescue NewRelic::CommandRunFailedError, NewRelic::CommandExecutableNotFoundError
53
- 'unknown'
53
+ NewRelic::UNKNOWN_LOWER
54
54
  end
55
55
  else
56
56
  RUBY_PLATFORM.downcase
@@ -40,10 +40,12 @@ module NewRelic
40
40
  SERVER_ADDRESS_KEY = 'server.address'
41
41
  SERVER_PORT_KEY = 'server.port'
42
42
  SPAN_KIND_KEY = 'span.kind'
43
+ STACKTRACE_KEY = 'code.stacktrace'
43
44
  ENTRY_POINT_KEY = 'nr.entryPoint'
44
45
  TRUSTED_PARENT_KEY = 'trustedParentId'
45
46
  TRACING_VENDORS_KEY = 'tracingVendors'
46
47
  TRANSACTION_NAME_KEY = 'transaction.name'
48
+ THREAD_ID_KEY = 'thread.id'
47
49
 
48
50
  # Strings for static values of the event structure
49
51
  EVENT_TYPE = 'Span'
@@ -121,6 +123,10 @@ module NewRelic
121
123
  agent_attributes[DB_STATEMENT_KEY] = truncate(segment.nosql_statement, DB_STATEMENT_MAX_BYTES)
122
124
  end
123
125
 
126
+ if segment.params[:backtrace]
127
+ agent_attributes[STACKTRACE_KEY] = segment.params[:backtrace]
128
+ end
129
+
124
130
  [intrinsics, custom_attributes(segment), agent_attributes.merge(agent_attributes(segment))]
125
131
  end
126
132
 
@@ -135,7 +141,8 @@ module NewRelic
135
141
  PRIORITY_KEY => segment.transaction.priority,
136
142
  TIMESTAMP_KEY => milliseconds_since_epoch(segment),
137
143
  DURATION_KEY => segment.duration,
138
- NAME_KEY => segment.name
144
+ NAME_KEY => segment.name,
145
+ THREAD_ID_KEY => segment.thread_id
139
146
  }
140
147
 
141
148
  # with infinite-tracing, transactions may or may not be sampled!
@@ -247,7 +247,7 @@ module NewRelic
247
247
  log_error('start_segment', exception)
248
248
  end
249
249
 
250
- UNKNOWN = 'Unknown'.freeze
250
+ UNKNOWN = NewRelic::UNKNOWN
251
251
  OTHER = 'other'.freeze
252
252
 
253
253
  # Creates and starts a datastore segment used to time
@@ -19,7 +19,7 @@ module NewRelic
19
19
  # after its parent. We will use the optimized exclusive duration
20
20
  # calculation in all other cases.
21
21
  #
22
- attr_reader :start_time, :end_time, :duration, :exclusive_duration, :guid, :starting_segment_key
22
+ attr_reader :start_time, :end_time, :duration, :exclusive_duration, :guid, :starting_segment_key, :thread_id
23
23
  attr_accessor :name, :parent, :children_time, :transaction, :transaction_name, :llm_event
24
24
  attr_writer :record_metrics, :record_scoped_metric, :record_on_finish
25
25
  attr_reader :noticed_error
@@ -30,6 +30,7 @@ module NewRelic
30
30
  def initialize(name = nil, start_time = nil)
31
31
  @name = name
32
32
  @starting_segment_key = NewRelic::Agent::Tracer.current_segment_key
33
+ @thread_id = Thread.current.object_id
33
34
  @transaction_name = nil
34
35
  @transaction = nil
35
36
  @guid = NewRelic::Agent::GuidGenerator.generate_guid
@@ -11,7 +11,7 @@ module NewRelic
11
11
  module Agent
12
12
  class Transaction
13
13
  class DatastoreSegment < Segment
14
- UNKNOWN = 'unknown'.freeze
14
+ UNKNOWN = NewRelic::UNKNOWN_LOWER
15
15
 
16
16
  attr_reader :product, :operation, :collection, :sql_statement, :nosql_statement, :host, :port_path_or_id
17
17
  attr_accessor :database_name, :record_sql
@@ -35,7 +35,7 @@ module NewRelic
35
35
  end
36
36
 
37
37
  def caller_transport_type
38
- @caller_transport_type ||= 'Unknown'
38
+ @caller_transport_type ||= NewRelic::UNKNOWN
39
39
  end
40
40
 
41
41
  def accept_transport_type_from_api(value)
@@ -127,7 +127,7 @@ module NewRelic
127
127
  def consume_message_synthetics_headers(headers)
128
128
  synthetics_header = headers[CrossAppTracing::NR_MESSAGE_BROKER_SYNTHETICS_HEADER]
129
129
  if synthetics_header and
130
- incoming_payload = ::JSON.load(deobfuscate(synthetics_header)) and
130
+ incoming_payload = ::JSON.parse(deobfuscate(synthetics_header)) and
131
131
  SyntheticsMonitor.is_valid_payload?(incoming_payload) and
132
132
  SyntheticsMonitor.is_supported_version?(incoming_payload) and
133
133
  SyntheticsMonitor.is_trusted?(incoming_payload)
@@ -167,7 +167,7 @@ module NewRelic
167
167
  return unless CrossAppTracing.trusted_valid_cross_app_id?(decoded_id)
168
168
 
169
169
  txn_header = headers[CrossAppTracing::NR_MESSAGE_BROKER_TXN_HEADER]
170
- txn_info = ::JSON.load(deobfuscate(txn_header))
170
+ txn_info = ::JSON.parse(deobfuscate(txn_header))
171
171
  payload = CrossAppPayload.new(decoded_id, transaction, txn_info)
172
172
 
173
173
  @cross_app_payload = payload
@@ -18,7 +18,7 @@ module NewRelic
18
18
  STREAM = 'Stream'.freeze
19
19
  TEMP = 'Temp'.freeze
20
20
  TOPIC = 'Topic'.freeze
21
- UNKNOWN = 'Unknown'.freeze
21
+ UNKNOWN = NewRelic::UNKNOWN
22
22
 
23
23
  DESTINATION_TYPES = [
24
24
  :exchange,
@@ -43,12 +43,7 @@ module NewRelic
43
43
  end
44
44
 
45
45
  if request_path
46
- destinations = if allow_other_headers?
47
- default_destinations
48
- else
49
- AttributeFilter::DST_TRANSACTION_TRACER | AttributeFilter::DST_ERROR_COLLECTOR
50
- end
51
- txn.add_agent_attribute(:'request.uri', request_path, destinations)
46
+ txn.add_agent_attribute(:'request.uri', request_path, default_destinations)
52
47
  end
53
48
 
54
49
  if accept
@@ -136,10 +136,8 @@ module NewRelic
136
136
 
137
137
  transaction.distributed_tracer.parent_transaction_id = payload.transaction_id
138
138
 
139
- unless payload.sampled.nil?
140
- transaction.sampled = payload.sampled
141
- transaction.priority = payload.priority if payload.priority
142
- end
139
+ determine_sampling_decision(payload, header_data.trace_parent['trace_flags'])
140
+
143
141
  NewRelic::Agent.increment_metric(ACCEPT_SUCCESS_METRIC)
144
142
  true
145
143
  rescue => e
@@ -148,6 +146,37 @@ module NewRelic
148
146
  false
149
147
  end
150
148
 
149
+ def determine_sampling_decision(payload, trace_flags)
150
+ if trace_flags == '01'
151
+ set_priority_and_sampled(NewRelic::Agent.config[:'distributed_tracing.sampler.remote_parent_sampled'], payload)
152
+ elsif trace_flags == '00'
153
+ set_priority_and_sampled(NewRelic::Agent.config[:'distributed_tracing.sampler.remote_parent_not_sampled'], payload)
154
+ else
155
+ use_nr_tracestate_sampled(payload)
156
+ end
157
+ rescue
158
+ use_nr_tracestate_sampled(payload)
159
+ end
160
+
161
+ def use_nr_tracestate_sampled(payload)
162
+ unless payload.sampled.nil?
163
+ transaction.sampled = payload.sampled
164
+ transaction.priority = payload.priority if payload.priority
165
+ end
166
+ end
167
+
168
+ def set_priority_and_sampled(config, payload)
169
+ if config == 'always_on'
170
+ transaction.sampled = true
171
+ transaction.priority = 2.0
172
+ elsif config == 'always_off'
173
+ transaction.sampled = false
174
+ transaction.priority = 0
175
+ else # default
176
+ use_nr_tracestate_sampled(payload)
177
+ end
178
+ end
179
+
151
180
  def ignore_trace_context?
152
181
  if trace_context_header_data
153
182
  NewRelic::Agent.increment_metric(IGNORE_MULTIPLE_ACCEPT_METRIC)
@@ -150,7 +150,7 @@ module NewRelic
150
150
  # rubocop:disable Style/SafeNavigation
151
151
  transaction_name = transaction_name = Tracer.current_transaction &&
152
152
  Tracer.current_transaction.best_name ||
153
- 'unknown'
153
+ NewRelic::UNKNOWN_LOWER
154
154
  # rubocop:enable Style/SafeNavigation
155
155
  NewRelic::Agent.logger.warn("Unable to calculate elapsed transaction time for #{transaction_name}")
156
156
  end