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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +56 -0
- data/lib/new_relic/agent/agent.rb +2 -0
- data/lib/new_relic/agent/configuration/default_source.rb +112 -85
- data/lib/new_relic/agent/configuration/manager.rb +5 -2
- data/lib/new_relic/agent/configuration/yaml_source.rb +2 -2
- data/lib/new_relic/agent/database.rb +1 -1
- data/lib/new_relic/agent/database_adapter.rb +1 -1
- data/lib/new_relic/agent/datastores/redis.rb +1 -1
- data/lib/new_relic/agent/distributed_tracing/cross_app_tracing.rb +1 -1
- data/lib/new_relic/agent/distributed_tracing.rb +2 -0
- data/lib/new_relic/agent/external.rb +2 -0
- data/lib/new_relic/agent/instrumentation/action_dispatch.rb +1 -1
- data/lib/new_relic/agent/instrumentation/action_dispatch_subscriber.rb +1 -1
- data/lib/new_relic/agent/instrumentation/action_mailbox.rb +1 -1
- data/lib/new_relic/agent/instrumentation/action_mailer.rb +1 -1
- data/lib/new_relic/agent/instrumentation/active_job.rb +1 -1
- data/lib/new_relic/agent/instrumentation/active_job_subscriber.rb +6 -2
- data/lib/new_relic/agent/instrumentation/active_record.rb +6 -4
- data/lib/new_relic/agent/instrumentation/active_record_helper.rb +2 -2
- data/lib/new_relic/agent/instrumentation/active_record_notifications.rb +11 -9
- data/lib/new_relic/agent/instrumentation/active_record_prepend.rb +2 -2
- data/lib/new_relic/agent/instrumentation/async_http.rb +1 -1
- data/lib/new_relic/agent/instrumentation/aws_sdk_kinesis/instrumentation.rb +1 -1
- data/lib/new_relic/agent/instrumentation/concurrent_ruby.rb +1 -1
- data/lib/new_relic/agent/instrumentation/controller_instrumentation.rb +4 -0
- data/lib/new_relic/agent/instrumentation/curb.rb +1 -1
- data/lib/new_relic/agent/instrumentation/elasticsearch/chain.rb +1 -2
- data/lib/new_relic/agent/instrumentation/elasticsearch.rb +1 -1
- data/lib/new_relic/agent/instrumentation/ethon.rb +1 -1
- data/lib/new_relic/agent/instrumentation/excon.rb +1 -1
- data/lib/new_relic/agent/instrumentation/fiber/chain.rb +1 -1
- data/lib/new_relic/agent/instrumentation/fiber/prepend.rb +1 -1
- data/lib/new_relic/agent/instrumentation/httpclient.rb +1 -4
- data/lib/new_relic/agent/instrumentation/httpx/instrumentation.rb +1 -1
- data/lib/new_relic/agent/instrumentation/httpx.rb +1 -1
- data/lib/new_relic/agent/instrumentation/logstasher.rb +1 -1
- data/lib/new_relic/agent/instrumentation/memcache/dalli.rb +1 -1
- data/lib/new_relic/agent/instrumentation/memcache/helper.rb +2 -2
- data/lib/new_relic/agent/instrumentation/memcache/instrumentation.rb +1 -1
- data/lib/new_relic/agent/instrumentation/memcache/prepend.rb +1 -1
- data/lib/new_relic/agent/instrumentation/mongodb_command_subscriber.rb +1 -1
- data/lib/new_relic/agent/instrumentation/net_http.rb +2 -1
- data/lib/new_relic/agent/instrumentation/rake.rb +1 -1
- data/lib/new_relic/agent/instrumentation/rdkafka/chain.rb +2 -2
- data/lib/new_relic/agent/instrumentation/rdkafka/prepend.rb +2 -2
- data/lib/new_relic/agent/instrumentation/redis/constants.rb +2 -2
- data/lib/new_relic/agent/instrumentation/resque.rb +2 -2
- data/lib/new_relic/agent/instrumentation/roda.rb +1 -1
- data/lib/new_relic/agent/instrumentation/ruby_kafka/prepend.rb +1 -1
- data/lib/new_relic/agent/instrumentation/ruby_openai.rb +2 -2
- data/lib/new_relic/agent/instrumentation/sidekiq/extensions/delayed_class.rb +1 -1
- data/lib/new_relic/agent/instrumentation/stripe.rb +1 -1
- data/lib/new_relic/agent/instrumentation/typhoeus/instrumentation.rb +2 -2
- data/lib/new_relic/agent/llm/chat_completion_summary.rb +1 -1
- data/lib/new_relic/agent/llm/embedding.rb +1 -1
- data/lib/new_relic/agent/local_log_decorator.rb +1 -1
- data/lib/new_relic/agent/logging.rb +1 -1
- data/lib/new_relic/agent/messaging.rb +5 -0
- data/lib/new_relic/agent/method_tracer.rb +3 -0
- data/lib/new_relic/agent/monitors/inbound_request_monitor.rb +1 -1
- data/lib/new_relic/agent/monitors/synthetics_monitor.rb +1 -1
- data/lib/new_relic/agent/new_relic_service/json_marshaller.rb +2 -2
- data/lib/new_relic/agent/opentelemetry/context/propagation/trace_propagator.rb +66 -0
- data/lib/new_relic/agent/opentelemetry/context/propagation.rb +15 -0
- data/lib/new_relic/agent/opentelemetry/context.rb +13 -0
- data/lib/new_relic/agent/opentelemetry/trace/span.rb +31 -0
- data/lib/new_relic/agent/opentelemetry/trace/tracer.rb +129 -0
- data/lib/new_relic/agent/opentelemetry/trace/tracer_provider.rb +18 -0
- data/lib/new_relic/agent/opentelemetry/trace.rb +15 -0
- data/lib/new_relic/agent/opentelemetry/transaction_patch.rb +69 -0
- data/lib/new_relic/agent/opentelemetry_bridge.rb +32 -0
- data/lib/new_relic/agent/parameter_filtering.rb +1 -1
- data/lib/new_relic/agent/samplers/cpu_sampler.rb +1 -1
- data/lib/new_relic/agent/samplers/memory_sampler.rb +1 -1
- data/lib/new_relic/agent/span_event_primitive.rb +8 -1
- data/lib/new_relic/agent/tracer.rb +1 -1
- data/lib/new_relic/agent/transaction/abstract_segment.rb +2 -1
- data/lib/new_relic/agent/transaction/datastore_segment.rb +1 -1
- data/lib/new_relic/agent/transaction/distributed_tracer.rb +3 -3
- data/lib/new_relic/agent/transaction/message_broker_segment.rb +1 -1
- data/lib/new_relic/agent/transaction/request_attributes.rb +1 -6
- data/lib/new_relic/agent/transaction/trace_context.rb +33 -4
- data/lib/new_relic/agent/transaction_time_aggregator.rb +1 -1
- data/lib/new_relic/agent/utilization/ecs.rb +22 -0
- data/lib/new_relic/agent/utilization/ecs_v4.rb +22 -0
- data/lib/new_relic/agent/utilization_data.rb +25 -0
- data/lib/new_relic/agent/vm/c_ruby_vm.rb +3 -3
- data/lib/new_relic/agent.rb +28 -0
- data/lib/new_relic/constants.rb +1 -0
- data/lib/new_relic/control/instrumentation.rb +1 -1
- data/lib/new_relic/dependency_detection.rb +0 -1
- data/lib/new_relic/helper.rb +7 -0
- data/lib/new_relic/version.rb +1 -1
- data/lib/sequel/extensions/new_relic_instrumentation.rb +1 -1
- data/lib/tasks/helpers/newrelicyml.rb +2 -0
- data/newrelic.yml +25 -15
- data/newrelic_rpm.gemspec +1 -1
- 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.
|
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}")
|
@@ -19,7 +19,7 @@ module NewRelic
|
|
19
19
|
def warn_for_yajl
|
20
20
|
if defined?(::Yajl)
|
21
21
|
require 'yajl/version'
|
22
|
-
if
|
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.
|
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) &&
|
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
|
@@ -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!
|
@@ -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 =
|
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 ||=
|
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.
|
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.
|
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
|
@@ -43,12 +43,7 @@ module NewRelic
|
|
43
43
|
end
|
44
44
|
|
45
45
|
if request_path
|
46
|
-
|
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
|
-
|
140
|
-
|
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
|
-
|
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
|