newrelic_rpm 9.17.0 → 9.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.build_ignore +1 -0
- data/CHANGELOG.md +105 -1
- data/lib/new_relic/agent/agent.rb +2 -0
- data/lib/new_relic/agent/agent_helpers/connect.rb +3 -3
- data/lib/new_relic/agent/agent_helpers/harvest.rb +3 -3
- data/lib/new_relic/agent/agent_helpers/shutdown.rb +1 -1
- data/lib/new_relic/agent/agent_helpers/start_worker_thread.rb +1 -1
- data/lib/new_relic/agent/agent_helpers/startup.rb +4 -4
- data/lib/new_relic/agent/configuration/default_source.rb +145 -120
- data/lib/new_relic/agent/configuration/manager.rb +5 -2
- data/lib/new_relic/agent/configuration/yaml_source.rb +4 -4
- 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/http_clients/uri_util.rb +1 -1
- 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/instrumentation.rb +1 -0
- 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/instrumentation.rb +3 -3
- data/lib/new_relic/agent/instrumentation/net_http.rb +2 -1
- data/lib/new_relic/agent/instrumentation/notifications_subscriber.rb +0 -2
- 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/delay_extensions.rb +24 -0
- data/lib/new_relic/agent/instrumentation/sidekiq/extensions/delayed_class.rb +1 -1
- data/lib/new_relic/agent/instrumentation/sidekiq.rb +9 -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/new_relic_service.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/{tasks/instrumentation_generator/templates/Envfile.tt → new_relic/agent/opentelemetry/context.rb} +9 -5
- 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/serverless_handler.rb +7 -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/tracing.rb +3 -3
- data/lib/new_relic/agent/transaction.rb +2 -1
- 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 +40 -5
- 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/instance_methods.rb +5 -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 +6 -2
- data/newrelic.yml +80 -43
- data/newrelic_rpm.gemspec +1 -1
- metadata +17 -19
- data/lib/tasks/instrumentation_generator/README.md +0 -63
- data/lib/tasks/instrumentation_generator/TODO.md +0 -33
- data/lib/tasks/instrumentation_generator/instrumentation.thor +0 -130
- data/lib/tasks/instrumentation_generator/templates/chain.tt +0 -21
- data/lib/tasks/instrumentation_generator/templates/chain_method.tt +0 -7
- data/lib/tasks/instrumentation_generator/templates/dependency_detection.tt +0 -32
- data/lib/tasks/instrumentation_generator/templates/instrumentation.tt +0 -13
- data/lib/tasks/instrumentation_generator/templates/instrumentation_method.tt +0 -3
- data/lib/tasks/instrumentation_generator/templates/newrelic.yml.tt +0 -19
- data/lib/tasks/instrumentation_generator/templates/prepend.tt +0 -13
- data/lib/tasks/instrumentation_generator/templates/prepend_method.tt +0 -3
- data/lib/tasks/instrumentation_generator/templates/test.tt +0 -15
@@ -8,11 +8,11 @@ module NewRelic
|
|
8
8
|
module Typhoeus
|
9
9
|
HYDRA_SEGMENT_NAME = 'External/Multiple/Typhoeus::Hydra/run'
|
10
10
|
NOTICEABLE_ERROR_CLASS = 'Typhoeus::Errors::TyphoeusError'
|
11
|
-
EARLIEST_VERSION =
|
11
|
+
EARLIEST_VERSION = '0.5.3'
|
12
12
|
INSTRUMENTATION_NAME = NewRelic::Agent.base_name(name)
|
13
13
|
|
14
14
|
def self.is_supported_version?
|
15
|
-
|
15
|
+
NewRelic::Helper.version_satisfied?(::Typhoeus::VERSION, '>=', EARLIEST_VERSION)
|
16
16
|
end
|
17
17
|
|
18
18
|
def self.request_is_hydra_enabled?(request)
|
@@ -32,7 +32,7 @@ module NewRelic
|
|
32
32
|
# TODO: OLD RUBIES < 2.6
|
33
33
|
# Hash#merge accepts multiple arguments in 2.6
|
34
34
|
# Remove condition once support for Ruby <2.6 is dropped
|
35
|
-
if RUBY_VERSION >= '2.6.0'
|
35
|
+
if NewRelic::Helper.version_satisfied?(RUBY_VERSION, '>=', '2.6.0')
|
36
36
|
LlmEvent::ATTRIBUTE_NAME_EXCEPTIONS.merge(ResponseHeaders::ATTRIBUTE_NAME_EXCEPTIONS, ATTRIBUTE_NAME_EXCEPTIONS)
|
37
37
|
else
|
38
38
|
LlmEvent::ATTRIBUTE_NAME_EXCEPTIONS.merge(ResponseHeaders::ATTRIBUTE_NAME_EXCEPTIONS).merge(ATTRIBUTE_NAME_EXCEPTIONS)
|
@@ -25,7 +25,7 @@ module NewRelic
|
|
25
25
|
# TODO: OLD RUBIES < 2.6
|
26
26
|
# Hash#merge accepts multiple arguments in 2.6
|
27
27
|
# Remove condition once support for Ruby <2.6 is dropped
|
28
|
-
if RUBY_VERSION >= '2.6.0'
|
28
|
+
if NewRelic::Helper.version_satisfied?(RUBY_VERSION, '>=', '2.6.0')
|
29
29
|
LlmEvent::ATTRIBUTE_NAME_EXCEPTIONS.merge(ResponseHeaders::ATTRIBUTE_NAME_EXCEPTIONS, ATTRIBUTE_NAME_EXCEPTIONS)
|
30
30
|
else
|
31
31
|
LlmEvent::ATTRIBUTE_NAME_EXCEPTIONS.merge(ResponseHeaders::ATTRIBUTE_NAME_EXCEPTIONS).merge(ATTRIBUTE_NAME_EXCEPTIONS)
|
@@ -43,7 +43,7 @@ module NewRelic
|
|
43
43
|
# URI version 1.0+ will ship with Ruby 3.4
|
44
44
|
# Once we drop support for Rubies below 3.4, we can use the
|
45
45
|
# URI::RFC2396 parser exclusively.
|
46
|
-
if
|
46
|
+
if NewRelic::Helper.version_satisfied?(URI::VERSION, '>=', '1.0')
|
47
47
|
URI::RFC2396_PARSER.escape(entity_name)
|
48
48
|
else
|
49
49
|
URI::DEFAULT_PARSER.escape(entity_name)
|
@@ -165,7 +165,7 @@ module NewRelic
|
|
165
165
|
# Positional and Keyword arguments are separated beginning with Ruby 2.7
|
166
166
|
# Signature of ::Logger constructor changes in Ruby 2.4 to have both positional and keyword args
|
167
167
|
# We pivot on Ruby 2.7 for widest supportability with least amount of hassle.
|
168
|
-
if RUBY_VERSION < '2.7.0'
|
168
|
+
if NewRelic::Helper.version_satisfied?(RUBY_VERSION, '<', '2.7.0')
|
169
169
|
def initialize(*args)
|
170
170
|
super(*args)
|
171
171
|
self.formatter = DecoratingFormatter.new
|
@@ -49,6 +49,7 @@ module NewRelic
|
|
49
49
|
#
|
50
50
|
# @return [NewRelic::Agent::Transaction::MessageBrokerSegment]
|
51
51
|
#
|
52
|
+
# @!scope class
|
52
53
|
# @api public
|
53
54
|
#
|
54
55
|
def start_message_broker_segment(action: nil,
|
@@ -107,6 +108,7 @@ module NewRelic
|
|
107
108
|
# @return return value of given block, which will be the same as the
|
108
109
|
# return value of an un-instrumented subscribed callback
|
109
110
|
#
|
111
|
+
# @!scope class
|
110
112
|
# @api public
|
111
113
|
#
|
112
114
|
def wrap_message_broker_consume_transaction(library:,
|
@@ -180,6 +182,7 @@ module NewRelic
|
|
180
182
|
#
|
181
183
|
# @return [NewRelic::Agent::Transaction::MessageBrokerSegment]
|
182
184
|
#
|
185
|
+
# @!scope class
|
183
186
|
# @api public
|
184
187
|
#
|
185
188
|
def start_amqp_publish_segment(library:,
|
@@ -240,6 +243,7 @@ module NewRelic
|
|
240
243
|
#
|
241
244
|
# @return [NewRelic::Agent::Transaction::MessageBrokerSegment]
|
242
245
|
#
|
246
|
+
# @!scope class
|
243
247
|
# @api public
|
244
248
|
#
|
245
249
|
def start_amqp_consume_segment(library:,
|
@@ -301,6 +305,7 @@ module NewRelic
|
|
301
305
|
# @return return value of given block, which will be the same as the
|
302
306
|
# return value of an un-instrumented subscribed callback
|
303
307
|
#
|
308
|
+
# @!scope class
|
304
309
|
# @api public
|
305
310
|
#
|
306
311
|
def wrap_amqp_consume_transaction(library: nil,
|
@@ -65,6 +65,7 @@ module NewRelic
|
|
65
65
|
# categories, but generally this *should never ever be done*. Most of the time you can aggregate
|
66
66
|
# on the server.
|
67
67
|
#
|
68
|
+
# @!scope class
|
68
69
|
# @api public
|
69
70
|
#
|
70
71
|
def trace_execution_scoped(metric_names, options = NewRelic::EMPTY_HASH) # THREAD_LOCAL_ACCESS
|
@@ -81,6 +82,7 @@ module NewRelic
|
|
81
82
|
#
|
82
83
|
# * <tt>metric_names</tt> is a single name or an array of names of metrics
|
83
84
|
#
|
85
|
+
# @!scope class
|
84
86
|
# @api public
|
85
87
|
#
|
86
88
|
def trace_execution_unscoped(metric_names, options = NewRelic::EMPTY_HASH) # THREAD_LOCAL_ACCESS
|
@@ -241,6 +243,7 @@ module NewRelic
|
|
241
243
|
# # Instrument foo in transaction traces only
|
242
244
|
# add_method_tracer :foo, 'Custom/foo', :metric => false
|
243
245
|
#
|
246
|
+
# @!scope class
|
244
247
|
# @api public
|
245
248
|
#
|
246
249
|
def add_method_tracer(method_name, metric_name = nil, options = {})
|
@@ -32,7 +32,7 @@ module NewRelic
|
|
32
32
|
|
33
33
|
def deserialize_header(encoded_header, key)
|
34
34
|
decoded_header = obfuscator.deobfuscate(encoded_header)
|
35
|
-
::JSON.
|
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
|
@@ -455,7 +455,7 @@ module NewRelic
|
|
455
455
|
end
|
456
456
|
|
457
457
|
def handle_error_response(response, endpoint)
|
458
|
-
NewRelic::Agent.agent
|
458
|
+
NewRelic::Agent.agent&.health_check&.update_status(NewRelic::Agent::HealthCheck::HTTP_ERROR, [response.code, endpoint])
|
459
459
|
|
460
460
|
case response
|
461
461
|
when Net::HTTPRequestTimeOut,
|
@@ -641,7 +641,7 @@ module NewRelic
|
|
641
641
|
response = relay_request(request, opts)
|
642
642
|
|
643
643
|
if response.is_a?(Net::HTTPSuccess) || response.is_a?(Net::HTTPAccepted)
|
644
|
-
NewRelic::Agent.agent
|
644
|
+
NewRelic::Agent.agent&.health_check&.update_status(NewRelic::Agent::HealthCheck::HEALTHY)
|
645
645
|
response
|
646
646
|
else
|
647
647
|
handle_error_response(response, opts[:endpoint])
|
@@ -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
|
@@ -2,8 +2,12 @@
|
|
2
2
|
# See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
|
3
3
|
# frozen_string_literal: true
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
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,7 +40,13 @@ module NewRelic
|
|
40
40
|
|
41
41
|
@event, @context = event, context
|
42
42
|
|
43
|
-
|
43
|
+
txn_name = function_name
|
44
|
+
if ENV['NEW_RELIC_APM_LAMBDA_MODE'] == 'true'
|
45
|
+
source = event_source_event_info['name'] if event_source_event_info
|
46
|
+
txn_name = "#{source.upcase} #{txn_name}" if source
|
47
|
+
end
|
48
|
+
|
49
|
+
NewRelic::Agent::Tracer.in_transaction(category: category, name: txn_name) do
|
44
50
|
prep_transaction
|
45
51
|
|
46
52
|
process_response(NewRelic::LanguageSupport.constantize(namespace)
|
@@ -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!
|