newrelic_rpm 8.5.0 → 8.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +6 -3
- data/.yardopts +1 -0
- data/CHANGELOG.md +127 -1
- data/CONTRIBUTING.md +1 -1
- data/LICENSE +0 -6
- data/README.md +17 -19
- data/Rakefile +26 -21
- data/THIRD_PARTY_NOTICES.md +14 -199
- data/docker-compose.yml +1 -1
- data/lib/new_relic/agent/agent.rb +26 -2
- data/lib/new_relic/agent/agent_logger.rb +7 -0
- data/lib/new_relic/agent/audit_logger.rb +4 -0
- data/lib/new_relic/agent/autostart.rb +13 -10
- data/lib/new_relic/agent/configuration/default_source.rb +217 -50
- data/lib/new_relic/agent/configuration/environment_source.rb +2 -0
- data/lib/new_relic/agent/configuration/event_harvest_config.rb +4 -2
- data/lib/new_relic/agent/configuration/server_source.rb +1 -0
- data/lib/new_relic/agent/database.rb +5 -5
- data/lib/new_relic/agent/datastores/metric_helper.rb +3 -1
- data/lib/new_relic/agent/hostname.rb +16 -10
- data/lib/new_relic/agent/instrumentation/action_controller_subscriber.rb +23 -20
- data/lib/new_relic/agent/instrumentation/active_job.rb +9 -2
- data/lib/new_relic/agent/instrumentation/active_merchant.rb +14 -0
- data/lib/new_relic/agent/instrumentation/active_record_helper.rb +22 -6
- data/lib/new_relic/agent/instrumentation/active_record_subscriber.rb +16 -7
- data/lib/new_relic/agent/instrumentation/active_support_logger/chain.rb +23 -0
- data/lib/new_relic/agent/instrumentation/active_support_logger/instrumentation.rb +20 -0
- data/lib/new_relic/agent/instrumentation/active_support_logger/prepend.rb +12 -0
- data/lib/new_relic/agent/instrumentation/active_support_logger.rb +24 -0
- data/lib/new_relic/agent/instrumentation/acts_as_solr.rb +10 -0
- data/lib/new_relic/agent/instrumentation/authlogic.rb +10 -0
- data/lib/new_relic/agent/instrumentation/controller_instrumentation.rb +9 -4
- data/lib/new_relic/agent/instrumentation/curb/chain.rb +1 -1
- data/lib/new_relic/agent/instrumentation/curb/prepend.rb +1 -1
- data/lib/new_relic/agent/instrumentation/data_mapper.rb +12 -0
- data/lib/new_relic/agent/instrumentation/delayed_job_instrumentation.rb +19 -0
- data/lib/new_relic/agent/instrumentation/logger/instrumentation.rb +18 -18
- data/lib/new_relic/agent/instrumentation/logger.rb +4 -3
- data/lib/new_relic/agent/instrumentation/rack/helpers.rb +2 -0
- data/lib/new_relic/agent/instrumentation/rainbows_instrumentation.rb +11 -0
- data/lib/new_relic/agent/instrumentation/sidekiq.rb +15 -0
- data/lib/new_relic/agent/instrumentation/sinatra.rb +21 -11
- data/lib/new_relic/agent/instrumentation/sunspot.rb +10 -0
- data/lib/new_relic/agent/instrumentation/thread/chain.rb +24 -0
- data/lib/new_relic/agent/instrumentation/thread/instrumentation.rb +27 -0
- data/lib/new_relic/agent/instrumentation/thread/prepend.rb +22 -0
- data/lib/new_relic/agent/instrumentation/thread.rb +20 -0
- data/lib/new_relic/agent/linking_metadata.rb +45 -0
- data/lib/new_relic/agent/local_log_decorator.rb +37 -0
- data/lib/new_relic/agent/log_event_aggregator.rb +234 -0
- data/lib/new_relic/agent/log_priority.rb +20 -0
- data/lib/new_relic/agent/method_tracer.rb +9 -4
- data/lib/new_relic/agent/method_tracer_helpers.rb +80 -0
- data/lib/new_relic/agent/new_relic_service.rb +27 -23
- data/lib/new_relic/agent/pipe_service.rb +5 -2
- data/lib/new_relic/agent/samplers/memory_sampler.rb +6 -1
- data/lib/new_relic/agent/span_event_primitive.rb +9 -6
- data/lib/new_relic/agent/stats.rb +48 -23
- data/lib/new_relic/agent/tracer.rb +14 -1
- data/lib/new_relic/agent/transaction/abstract_segment.rb +29 -1
- data/lib/new_relic/agent/transaction/tracing.rb +8 -3
- data/lib/new_relic/agent/transaction.rb +47 -11
- data/lib/new_relic/agent/transaction_error_primitive.rb +2 -0
- data/lib/new_relic/agent/transaction_metrics.rb +5 -4
- data/lib/new_relic/agent/vm/mri_vm.rb +13 -1
- data/lib/new_relic/agent.rb +6 -14
- data/lib/new_relic/control/instrumentation.rb +31 -0
- data/lib/new_relic/dependency_detection.rb +1 -1
- data/lib/new_relic/helper.rb +40 -0
- data/lib/new_relic/language_support.rb +17 -0
- data/lib/new_relic/local_environment.rb +2 -0
- data/lib/new_relic/supportability_helper.rb +1 -0
- data/lib/new_relic/traced_thread.rb +35 -0
- data/lib/new_relic/version.rb +1 -1
- data/lib/tasks/config.rake +13 -5
- data/newrelic.yml +34 -4
- data/newrelic_rpm.gemspec +2 -3
- data/test/agent_helper.rb +18 -4
- metadata +30 -17
- data/ROADMAP.md +0 -24
@@ -15,23 +15,23 @@ module NewRelic
|
|
15
15
|
class NewRelicService
|
16
16
|
# Specifies the version of the agent's communication protocol with
|
17
17
|
# the NewRelic hosted site.
|
18
|
-
|
19
18
|
PROTOCOL_VERSION = 17
|
20
19
|
|
21
|
-
# 1f147a42: v10 (tag 3.5.3.17)
|
22
|
-
# cf0d1ff1: v9 (tag 3.5.0)
|
23
|
-
# 14105: v8 (tag 2.10.3)
|
24
|
-
# (no v7)
|
25
|
-
# 10379: v6 (not tagged)
|
26
|
-
# 4078: v5 (tag 2.5.4)
|
27
|
-
# 2292: v4 (tag 2.3.6)
|
28
|
-
# 1754: v3 (tag 2.3.0)
|
29
|
-
# 534: v2 (shows up in 2.1.0, our first tag)
|
30
|
-
|
31
20
|
# These include Errno connection errors, and all indicate that the
|
32
21
|
# underlying TCP connection may be in a bad state.
|
33
22
|
CONNECTION_ERRORS = [Timeout::Error, EOFError, SystemCallError, SocketError].freeze
|
34
23
|
|
24
|
+
# Don't perform compression on the payload unless its uncompressed size is
|
25
|
+
# greater than or equal to this number of bytes. In testing with
|
26
|
+
# Ruby 2.2 - 3.1, we determined an absolute minimum value for ASCII to be
|
27
|
+
# 535 bytes to obtain at least a 10% savings in size. It is recommended
|
28
|
+
# that this value be kept above that 535 number. It is also important to
|
29
|
+
# consider the CPU cost involved with performing compression and to find
|
30
|
+
# a balance between CPU cycles spent and bandwidth saved. A good
|
31
|
+
# reasonable default here is 2048 bytes, which is a tried and true Apache
|
32
|
+
# Tomcat default (as of v8.5.78)
|
33
|
+
MIN_BYTE_SIZE_TO_COMPRESS = 2048
|
34
|
+
|
35
35
|
attr_accessor :request_timeout
|
36
36
|
attr_reader :collector, :marshaller, :agent_id
|
37
37
|
|
@@ -177,26 +177,30 @@ module NewRelic
|
|
177
177
|
:item_count => items.size)
|
178
178
|
end
|
179
179
|
|
180
|
+
def log_event_data(data)
|
181
|
+
payload, size = LogEventAggregator.payload_to_melt_format(data)
|
182
|
+
invoke_remote(:log_event_data, payload, :item_count => size)
|
183
|
+
end
|
184
|
+
|
180
185
|
def error_event_data(data)
|
181
186
|
metadata, items = data
|
182
|
-
invoke_remote(:error_event_data, [@agent_id, *data], :item_count => items.size)
|
187
|
+
response = invoke_remote(:error_event_data, [@agent_id, *data], :item_count => items.size)
|
183
188
|
NewRelic::Agent.record_metric("Supportability/Events/TransactionError/Sent", :count => items.size)
|
184
189
|
NewRelic::Agent.record_metric("Supportability/Events/TransactionError/Seen", :count => metadata[:events_seen])
|
190
|
+
response
|
185
191
|
end
|
186
192
|
|
187
193
|
def span_event_data(data)
|
188
194
|
metadata, items = data
|
189
|
-
invoke_remote(:span_event_data, [@agent_id, *data], :item_count => items.size)
|
195
|
+
response = invoke_remote(:span_event_data, [@agent_id, *data], :item_count => items.size)
|
190
196
|
NewRelic::Agent.record_metric("Supportability/Events/SpanEvents/Sent", :count => items.size)
|
191
197
|
NewRelic::Agent.record_metric("Supportability/Events/SpanEvents/Seen", :count => metadata[:events_seen])
|
198
|
+
response
|
192
199
|
end
|
193
200
|
|
194
|
-
# We do not compress if content is smaller than 64kb. There are
|
195
|
-
# problems with bugs in Ruby in some versions that expose us
|
196
|
-
# to a risk of segfaults if we compress aggressively.
|
197
201
|
def compress_request_if_needed(data, endpoint)
|
198
202
|
encoding = 'identity'
|
199
|
-
if data.size
|
203
|
+
if data.size >= MIN_BYTE_SIZE_TO_COMPRESS
|
200
204
|
encoding = Agent.config[:compressed_content_encoding]
|
201
205
|
data = if encoding == 'deflate'
|
202
206
|
Encoders::Compressed::Deflate.encode(data)
|
@@ -422,8 +426,8 @@ module NewRelic
|
|
422
426
|
end
|
423
427
|
serialize_finish_ts = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
424
428
|
|
429
|
+
size = data.size # only the uncompressed size is reported
|
425
430
|
data, encoding = compress_request_if_needed(data, method)
|
426
|
-
size = data.size
|
427
431
|
|
428
432
|
# Preconnect needs to always use the configured collector host, not the redirect host
|
429
433
|
# We reset it here so we are always using the configured collector during our creation of the new connection
|
@@ -454,7 +458,7 @@ module NewRelic
|
|
454
458
|
def handle_serialization_error(method, e)
|
455
459
|
NewRelic::Agent.increment_metric("Supportability/serialization_failure")
|
456
460
|
NewRelic::Agent.increment_metric("Supportability/serialization_failure/#{method}")
|
457
|
-
msg = "Failed to serialize #{method} data using #{@marshaller.class
|
461
|
+
msg = "Failed to serialize #{method} data using #{@marshaller.class}: #{e.inspect}"
|
458
462
|
error = SerializationError.new(msg)
|
459
463
|
error.set_backtrace(e.backtrace)
|
460
464
|
raise error
|
@@ -464,11 +468,11 @@ module NewRelic
|
|
464
468
|
serialize_time = serialize_finish_ts && (serialize_finish_ts - start_ts)
|
465
469
|
request_duration = response_check_ts && (response_check_ts - request_send_ts)
|
466
470
|
if request_duration
|
467
|
-
NewRelic::Agent.record_metric("Supportability/Agent/Collector/#{method
|
471
|
+
NewRelic::Agent.record_metric("Supportability/Agent/Collector/#{method}/Duration", request_duration)
|
468
472
|
end
|
469
473
|
if serialize_time
|
470
474
|
NewRelic::Agent.record_metric("Supportability/invoke_remote_serialize", serialize_time)
|
471
|
-
NewRelic::Agent.record_metric("Supportability/invoke_remote_serialize/#{method
|
475
|
+
NewRelic::Agent.record_metric("Supportability/invoke_remote_serialize/#{method}", serialize_time)
|
472
476
|
end
|
473
477
|
end
|
474
478
|
|
@@ -482,8 +486,8 @@ module NewRelic
|
|
482
486
|
# of items as arguments.
|
483
487
|
def record_size_supportability_metrics(method, size_bytes, item_count)
|
484
488
|
metrics = [
|
485
|
-
"Supportability/
|
486
|
-
"Supportability/
|
489
|
+
"Supportability/Ruby/Collector/Output/Bytes",
|
490
|
+
"Supportability/Ruby/Collector/#{method}/Output/Bytes"
|
487
491
|
]
|
488
492
|
# we may not have an item count, in which case, just record 0 for the exclusive time
|
489
493
|
item_count ||= 0
|
@@ -10,8 +10,7 @@ module NewRelic
|
|
10
10
|
|
11
11
|
def initialize(channel_id)
|
12
12
|
@channel_id = channel_id
|
13
|
-
@collector = NewRelic::Control::Server.new(:
|
14
|
-
:port => 0)
|
13
|
+
@collector = NewRelic::Control::Server.new({name: 'parent', port: 0})
|
15
14
|
@pipe = NewRelic::Agent::PipeChannelManager.channels[@channel_id]
|
16
15
|
if @pipe && @pipe.parent_pid != $$
|
17
16
|
@pipe.after_fork_in_child
|
@@ -61,6 +60,10 @@ module NewRelic
|
|
61
60
|
write_to_pipe(:sql_trace_data, sql) if sql
|
62
61
|
end
|
63
62
|
|
63
|
+
def log_event_data(logs)
|
64
|
+
write_to_pipe(:log_event_data, logs) if logs
|
65
|
+
end
|
66
|
+
|
64
67
|
def shutdown
|
65
68
|
@pipe.close if @pipe
|
66
69
|
end
|
@@ -3,6 +3,7 @@
|
|
3
3
|
# See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
|
4
4
|
|
5
5
|
require 'new_relic/agent/sampler'
|
6
|
+
require 'new_relic/helper'
|
6
7
|
|
7
8
|
module NewRelic
|
8
9
|
module Agent
|
@@ -46,7 +47,11 @@ module NewRelic
|
|
46
47
|
|
47
48
|
def self.platform
|
48
49
|
if RUBY_PLATFORM =~ /java/
|
49
|
-
|
50
|
+
begin
|
51
|
+
NewRelic::Helper.run_command('uname -s').downcase
|
52
|
+
rescue NewRelic::CommandRunFailedError, NewRelic::CommandExecutableNotFoundError
|
53
|
+
'unknown'
|
54
|
+
end
|
50
55
|
else
|
51
56
|
RUBY_PLATFORM.downcase
|
52
57
|
end
|
@@ -165,18 +165,21 @@ module NewRelic
|
|
165
165
|
end
|
166
166
|
end
|
167
167
|
|
168
|
-
def
|
169
|
-
return
|
170
|
-
return
|
171
|
-
|
168
|
+
def merge_hashes(hash1, hash2)
|
169
|
+
return hash1 if hash2.nil? || hash2.empty?
|
170
|
+
return hash2 if hash1.nil? || hash1.empty?
|
171
|
+
|
172
|
+
hash1.merge!(hash2)
|
172
173
|
end
|
173
174
|
|
174
175
|
def agent_attributes segment
|
175
176
|
agent_attributes = segment.attributes
|
176
177
|
.agent_attributes_for(NewRelic::Agent::AttributeFilter::DST_SPAN_EVENTS)
|
177
178
|
error_attributes = error_attributes(segment)
|
178
|
-
|
179
|
-
|
179
|
+
code_attributes = segment.code_attributes
|
180
|
+
agent_attributes = merge_hashes(agent_attributes, error_attributes)
|
181
|
+
agent_attributes = merge_hashes(agent_attributes, code_attributes)
|
182
|
+
agent_attributes.freeze
|
180
183
|
end
|
181
184
|
|
182
185
|
def parent_guid segment
|
@@ -4,6 +4,8 @@
|
|
4
4
|
module NewRelic
|
5
5
|
module Agent
|
6
6
|
class Stats
|
7
|
+
SKIP_MARSHALLING = [:@lock]
|
8
|
+
|
7
9
|
attr_accessor :call_count
|
8
10
|
attr_accessor :min_call_time
|
9
11
|
attr_accessor :max_call_time
|
@@ -12,6 +14,7 @@ module NewRelic
|
|
12
14
|
attr_accessor :sum_of_squares
|
13
15
|
|
14
16
|
def initialize
|
17
|
+
@lock = Mutex.new
|
15
18
|
reset
|
16
19
|
end
|
17
20
|
|
@@ -34,12 +37,14 @@ module NewRelic
|
|
34
37
|
end
|
35
38
|
|
36
39
|
def merge!(other)
|
37
|
-
@
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
40
|
+
@lock.synchronize do
|
41
|
+
@min_call_time = other.min_call_time if min_time_less?(other)
|
42
|
+
@max_call_time = other.max_call_time if other.max_call_time > max_call_time
|
43
|
+
@total_call_time += other.total_call_time
|
44
|
+
@total_exclusive_time += other.total_exclusive_time
|
45
|
+
@sum_of_squares += other.sum_of_squares
|
46
|
+
@call_count += other.call_count
|
47
|
+
end
|
43
48
|
self
|
44
49
|
end
|
45
50
|
|
@@ -78,13 +83,15 @@ module NewRelic
|
|
78
83
|
# will aggregate all data points collected over a specified period and upload
|
79
84
|
# its data to the NewRelic server
|
80
85
|
def record_data_point(value, exclusive_time = value)
|
81
|
-
@
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
86
|
+
@lock.synchronize do
|
87
|
+
@call_count += 1
|
88
|
+
@total_call_time += value
|
89
|
+
@min_call_time = value if value < @min_call_time || @call_count == 1
|
90
|
+
@max_call_time = value if value > @max_call_time
|
91
|
+
@total_exclusive_time += exclusive_time
|
92
|
+
|
93
|
+
@sum_of_squares += (value * value)
|
94
|
+
end
|
88
95
|
self
|
89
96
|
end
|
90
97
|
|
@@ -92,7 +99,7 @@ module NewRelic
|
|
92
99
|
|
93
100
|
# increments the call_count by one
|
94
101
|
def increment_count(value = 1)
|
95
|
-
@call_count += value
|
102
|
+
@lock.synchronize { @call_count += value }
|
96
103
|
end
|
97
104
|
|
98
105
|
# Concerned about implicit usage of inspect relying on stats format, so
|
@@ -122,17 +129,35 @@ module NewRelic
|
|
122
129
|
alias_method :apdex_f, :total_exclusive_time
|
123
130
|
|
124
131
|
def record_apdex(bucket, apdex_t)
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
132
|
+
@lock.synchronize do
|
133
|
+
case bucket
|
134
|
+
when :apdex_s then @call_count += 1
|
135
|
+
when :apdex_t then @total_call_time += 1
|
136
|
+
when :apdex_f then @total_exclusive_time += 1
|
137
|
+
end
|
138
|
+
if apdex_t
|
139
|
+
@min_call_time = apdex_t
|
140
|
+
@max_call_time = apdex_t
|
141
|
+
else
|
142
|
+
::NewRelic::Agent.logger.warn("Attempted to set apdex_t to #{apdex_t.inspect}, backtrace = #{caller.join("\n")}")
|
143
|
+
end
|
129
144
|
end
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
145
|
+
end
|
146
|
+
|
147
|
+
# Override marshalling methods to exclude @lock from being included in marshalled data
|
148
|
+
def marshal_dump
|
149
|
+
instance_variables.each_with_object({}) do |name, instance_copy|
|
150
|
+
next if SKIP_MARSHALLING.include?(name)
|
151
|
+
instance_copy[name] = instance_variable_get(name)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def marshal_load(marshalled_data)
|
156
|
+
marshalled_data.each do |name, value|
|
157
|
+
instance_variable_set(name, value) unless SKIP_MARSHALLING.include?(name)
|
135
158
|
end
|
159
|
+
# since the lock is excluded when marshalling, create a new lock when loading marshalled data
|
160
|
+
@lock = Mutex.new
|
136
161
|
end
|
137
162
|
|
138
163
|
protected
|
@@ -405,6 +405,19 @@ module NewRelic
|
|
405
405
|
|
406
406
|
alias_method :tl_clear, :clear_state
|
407
407
|
|
408
|
+
def thread_block_with_current_transaction(*args, &block)
|
409
|
+
current_txn = ::Thread.current[:newrelic_tracer_state].current_transaction if ::Thread.current[:newrelic_tracer_state]
|
410
|
+
Proc.new do
|
411
|
+
begin
|
412
|
+
NewRelic::Agent::Tracer.state.current_transaction = current_txn
|
413
|
+
segment = NewRelic::Agent::Tracer.start_segment(name: "Ruby/Thread/#{::Thread.current.object_id}")
|
414
|
+
block.call(*args) if block.respond_to?(:call)
|
415
|
+
ensure
|
416
|
+
segment.finish if segment
|
417
|
+
end
|
418
|
+
end
|
419
|
+
end
|
420
|
+
|
408
421
|
private
|
409
422
|
|
410
423
|
def start_and_add_segment segment, parent = nil
|
@@ -445,7 +458,7 @@ module NewRelic
|
|
445
458
|
end
|
446
459
|
|
447
460
|
# Current transaction stack
|
448
|
-
|
461
|
+
attr_accessor :current_transaction
|
449
462
|
|
450
463
|
# Execution tracing on current thread
|
451
464
|
attr_accessor :untraced
|
@@ -20,13 +20,14 @@ module NewRelic
|
|
20
20
|
# after its parent. We will use the optimized exclusive duration
|
21
21
|
# calculation in all other cases.
|
22
22
|
#
|
23
|
-
attr_reader :start_time, :end_time, :duration, :exclusive_duration, :guid
|
23
|
+
attr_reader :start_time, :end_time, :duration, :exclusive_duration, :guid, :starting_thread_id
|
24
24
|
attr_accessor :name, :parent, :children_time, :transaction, :transaction_name
|
25
25
|
attr_writer :record_metrics, :record_scoped_metric, :record_on_finish
|
26
26
|
attr_reader :noticed_error
|
27
27
|
|
28
28
|
def initialize name = nil, start_time = nil
|
29
29
|
@name = name
|
30
|
+
@starting_thread_id = ::Thread.current.object_id
|
30
31
|
@transaction_name = nil
|
31
32
|
@transaction = nil
|
32
33
|
@guid = NewRelic::Agent::GuidGenerator.generate_guid
|
@@ -45,6 +46,10 @@ module NewRelic
|
|
45
46
|
@record_scoped_metric = true
|
46
47
|
@record_on_finish = false
|
47
48
|
@noticed_error = nil
|
49
|
+
@code_filepath = nil
|
50
|
+
@code_function = nil
|
51
|
+
@code_lineno = nil
|
52
|
+
@code_namespace = nil
|
48
53
|
end
|
49
54
|
|
50
55
|
def start
|
@@ -56,6 +61,7 @@ module NewRelic
|
|
56
61
|
def finish
|
57
62
|
@end_time = Process.clock_gettime(Process::CLOCK_REALTIME)
|
58
63
|
@duration = end_time - start_time
|
64
|
+
|
59
65
|
return unless transaction
|
60
66
|
run_complete_callbacks
|
61
67
|
finalize if record_on_finish?
|
@@ -109,6 +115,28 @@ module NewRelic
|
|
109
115
|
@concurrent_children
|
110
116
|
end
|
111
117
|
|
118
|
+
def code_information=(info = {})
|
119
|
+
return unless info[:filepath]
|
120
|
+
|
121
|
+
@code_filepath = info[:filepath]
|
122
|
+
@code_function = info[:function]
|
123
|
+
@code_lineno = info[:lineno]
|
124
|
+
@code_namespace = info[:namespace]
|
125
|
+
end
|
126
|
+
|
127
|
+
def all_code_information_present?
|
128
|
+
@code_filepath && @code_function && @code_lineno && @code_namespace
|
129
|
+
end
|
130
|
+
|
131
|
+
def code_attributes
|
132
|
+
return ::NewRelic::EMPTY_HASH unless all_code_information_present?
|
133
|
+
|
134
|
+
@code_attributes ||= {'code.filepath' => @code_filepath,
|
135
|
+
'code.function' => @code_function,
|
136
|
+
'code.lineno' => @code_lineno,
|
137
|
+
'code.namespace' => @code_namespace}
|
138
|
+
end
|
139
|
+
|
112
140
|
INSPECT_IGNORE = [:@transaction, :@transaction_state].freeze
|
113
141
|
|
114
142
|
def inspect
|
@@ -6,7 +6,7 @@ module NewRelic
|
|
6
6
|
module Agent
|
7
7
|
class Transaction
|
8
8
|
module Tracing
|
9
|
-
attr_reader :
|
9
|
+
attr_reader :current_segment_by_thread
|
10
10
|
|
11
11
|
def async?
|
12
12
|
@async ||= false
|
@@ -23,7 +23,7 @@ module NewRelic
|
|
23
23
|
def add_segment segment, parent = nil
|
24
24
|
segment.transaction = self
|
25
25
|
segment.parent = parent || current_segment
|
26
|
-
|
26
|
+
set_current_segment segment
|
27
27
|
if @segments.length < segment_limit
|
28
28
|
@segments << segment
|
29
29
|
else
|
@@ -34,7 +34,12 @@ module NewRelic
|
|
34
34
|
end
|
35
35
|
|
36
36
|
def segment_complete segment
|
37
|
-
|
37
|
+
# if parent was in another thread, remove the current_segment entry for this thread
|
38
|
+
if segment.parent && segment.parent.starting_thread_id != ::Thread.current.object_id
|
39
|
+
remove_current_segment_by_thread_id(::Thread.current.object_id)
|
40
|
+
else
|
41
|
+
set_current_segment segment.parent
|
42
|
+
end
|
38
43
|
end
|
39
44
|
|
40
45
|
def segment_limit
|
@@ -77,6 +77,7 @@ module NewRelic
|
|
77
77
|
|
78
78
|
attr_reader :guid,
|
79
79
|
:metrics,
|
80
|
+
:logs,
|
80
81
|
:gc_start_snapshot,
|
81
82
|
:category,
|
82
83
|
:attributes,
|
@@ -123,7 +124,7 @@ module NewRelic
|
|
123
124
|
txn = Transaction.new(category, options)
|
124
125
|
state.reset(txn)
|
125
126
|
txn.state = state
|
126
|
-
txn.start
|
127
|
+
txn.start(options)
|
127
128
|
txn
|
128
129
|
end
|
129
130
|
|
@@ -216,7 +217,8 @@ module NewRelic
|
|
216
217
|
|
217
218
|
def initialize(category, options)
|
218
219
|
@nesting_max_depth = 0
|
219
|
-
@
|
220
|
+
@current_segment_by_thread = {}
|
221
|
+
@current_segment_lock = Mutex.new
|
220
222
|
@segments = []
|
221
223
|
|
222
224
|
self.default_name = options[:transaction_name]
|
@@ -236,6 +238,7 @@ module NewRelic
|
|
236
238
|
|
237
239
|
@exceptions = {}
|
238
240
|
@metrics = TransactionMetrics.new
|
241
|
+
@logs = PrioritySampledBuffer.new(NewRelic::Agent.instance.log_event_aggregator.capacity)
|
239
242
|
@guid = NewRelic::Agent::GuidGenerator.generate_guid
|
240
243
|
|
241
244
|
@ignore_this_transaction = false
|
@@ -259,6 +262,25 @@ module NewRelic
|
|
259
262
|
end
|
260
263
|
end
|
261
264
|
|
265
|
+
def parent_thread_id
|
266
|
+
::Thread.current.nr_parent_thread_id if ::Thread.current.respond_to?(:nr_parent_thread_id)
|
267
|
+
end
|
268
|
+
|
269
|
+
def current_segment
|
270
|
+
current_thread_id = ::Thread.current.object_id
|
271
|
+
return current_segment_by_thread[current_thread_id] if current_segment_by_thread[current_thread_id]
|
272
|
+
return current_segment_by_thread[parent_thread_id] if current_segment_by_thread[parent_thread_id]
|
273
|
+
current_segment_by_thread[@starting_thread_id]
|
274
|
+
end
|
275
|
+
|
276
|
+
def set_current_segment(new_segment)
|
277
|
+
@current_segment_lock.synchronize { current_segment_by_thread[::Thread.current.object_id] = new_segment }
|
278
|
+
end
|
279
|
+
|
280
|
+
def remove_current_segment_by_thread_id(id)
|
281
|
+
@current_segment_lock.synchronize { current_segment_by_thread.delete(id) }
|
282
|
+
end
|
283
|
+
|
262
284
|
def distributed_tracer
|
263
285
|
@distributed_tracer ||= DistributedTracer.new(self)
|
264
286
|
end
|
@@ -392,7 +414,7 @@ module NewRelic
|
|
392
414
|
@frozen_name ? true : false
|
393
415
|
end
|
394
416
|
|
395
|
-
def start
|
417
|
+
def start(options = {})
|
396
418
|
return if !state.is_execution_traced?
|
397
419
|
|
398
420
|
sql_sampler.on_start_transaction(state, request_path)
|
@@ -401,7 +423,7 @@ module NewRelic
|
|
401
423
|
|
402
424
|
ignore! if user_defined_rules_ignore?
|
403
425
|
|
404
|
-
create_initial_segment
|
426
|
+
create_initial_segment(options)
|
405
427
|
Segment.merge_untrusted_agent_attributes \
|
406
428
|
@filtered_params,
|
407
429
|
:'request.parameters',
|
@@ -412,12 +434,12 @@ module NewRelic
|
|
412
434
|
segments.first
|
413
435
|
end
|
414
436
|
|
415
|
-
def create_initial_segment
|
416
|
-
segment = create_segment @default_name
|
437
|
+
def create_initial_segment(options = {})
|
438
|
+
segment = create_segment @default_name, options
|
417
439
|
segment.record_scoped_metric = false
|
418
440
|
end
|
419
441
|
|
420
|
-
def create_segment(name)
|
442
|
+
def create_segment(name, options = {})
|
421
443
|
summary_metrics = nil
|
422
444
|
|
423
445
|
if name.start_with?(MIDDLEWARE_PREFIX)
|
@@ -431,6 +453,10 @@ module NewRelic
|
|
431
453
|
unscoped_metrics: summary_metrics
|
432
454
|
)
|
433
455
|
|
456
|
+
# #code_information will glean the code info out of the options hash
|
457
|
+
# if it exists or noop otherwise
|
458
|
+
segment.code_information = options
|
459
|
+
|
434
460
|
segment
|
435
461
|
end
|
436
462
|
|
@@ -445,7 +471,8 @@ module NewRelic
|
|
445
471
|
|
446
472
|
nest_initial_segment if segments.length == 1
|
447
473
|
nested_name = self.class.nested_transaction_name options[:transaction_name]
|
448
|
-
|
474
|
+
|
475
|
+
segment = create_segment nested_name, options
|
449
476
|
set_default_transaction_name(options[:transaction_name], category)
|
450
477
|
segment
|
451
478
|
end
|
@@ -533,6 +560,7 @@ module NewRelic
|
|
533
560
|
|
534
561
|
record_exceptions
|
535
562
|
record_transaction_event
|
563
|
+
record_log_events
|
536
564
|
merge_metrics
|
537
565
|
send_transaction_finished_event
|
538
566
|
end
|
@@ -716,9 +744,9 @@ module NewRelic
|
|
716
744
|
# Do not call this. Invoke the class method instead.
|
717
745
|
def notice_error(error, options = {}) # :nodoc:
|
718
746
|
# Only the last error is kept
|
719
|
-
if
|
720
|
-
|
721
|
-
options[:span_id] =
|
747
|
+
if current_segment
|
748
|
+
current_segment.notice_error(error, expected: options[:expected])
|
749
|
+
options[:span_id] = current_segment.guid
|
722
750
|
end
|
723
751
|
|
724
752
|
if @exceptions[error]
|
@@ -732,6 +760,10 @@ module NewRelic
|
|
732
760
|
agent.transaction_event_recorder.record payload
|
733
761
|
end
|
734
762
|
|
763
|
+
def record_log_events
|
764
|
+
agent.log_event_aggregator.record_batch self, @logs.to_a
|
765
|
+
end
|
766
|
+
|
735
767
|
def queue_time
|
736
768
|
@apdex_start ? @start_time - @apdex_start : 0
|
737
769
|
end
|
@@ -826,6 +858,10 @@ module NewRelic
|
|
826
858
|
attributes.merge_custom_attributes(p)
|
827
859
|
end
|
828
860
|
|
861
|
+
def add_log_event(event)
|
862
|
+
logs.append(event: event)
|
863
|
+
end
|
864
|
+
|
829
865
|
def recording_web_transaction?
|
830
866
|
web_category?(@category)
|
831
867
|
end
|
@@ -63,6 +63,8 @@ module NewRelic
|
|
63
63
|
append_cat payload, attrs
|
64
64
|
DistributedTraceAttributes.copy_to_hash payload, attrs
|
65
65
|
PayloadMetricMapping.append_mapped_metrics payload[:metrics], attrs
|
66
|
+
else
|
67
|
+
attrs[PRIORITY_KEY] = rand.round(NewRelic::PRIORITY_PRECISION)
|
66
68
|
end
|
67
69
|
|
68
70
|
attrs
|
@@ -13,6 +13,7 @@ module NewRelic
|
|
13
13
|
DEFAULT_PROC = Proc.new { |hash, name| hash[name] = NewRelic::Agent::Stats.new }
|
14
14
|
|
15
15
|
def initialize
|
16
|
+
@lock = Mutex.new
|
16
17
|
@unscoped = Hash.new(&DEFAULT_PROC)
|
17
18
|
@scoped = Hash.new(&DEFAULT_PROC)
|
18
19
|
end
|
@@ -42,11 +43,11 @@ module NewRelic
|
|
42
43
|
end
|
43
44
|
|
44
45
|
def each_unscoped
|
45
|
-
@unscoped.each { |name, stats| yield name, stats }
|
46
|
+
@lock.synchronize { @unscoped.each { |name, stats| yield name, stats } }
|
46
47
|
end
|
47
48
|
|
48
49
|
def each_scoped
|
49
|
-
@scoped.each { |name, stats| yield name, stats }
|
50
|
+
@lock.synchronize { @scoped.each { |name, stats| yield name, stats } }
|
50
51
|
end
|
51
52
|
|
52
53
|
def _record_metrics(names, value, aux, target, &blk)
|
@@ -54,10 +55,10 @@ module NewRelic
|
|
54
55
|
case names
|
55
56
|
when Array
|
56
57
|
names.each do |name|
|
57
|
-
target[name].record(value, aux, &blk)
|
58
|
+
@lock.synchronize { target[name].record(value, aux, &blk) }
|
58
59
|
end
|
59
60
|
else
|
60
|
-
target[names].record(value, aux, &blk)
|
61
|
+
@lock.synchronize { target[names].record(value, aux, &blk) }
|
61
62
|
end
|
62
63
|
end
|
63
64
|
end
|
@@ -49,7 +49,19 @@ module NewRelic
|
|
49
49
|
end
|
50
50
|
|
51
51
|
if supports?(:constant_cache_invalidations)
|
52
|
-
snap.constant_cache_invalidations =
|
52
|
+
snap.constant_cache_invalidations = gather_constant_cache_invalidations
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def gather_constant_cache_invalidations
|
57
|
+
# Ruby >= 3.2 uses :constant_cache
|
58
|
+
# see: https://github.com/ruby/ruby/pull/5433 and https://bugs.ruby-lang.org/issues/18589
|
59
|
+
# TODO: now that 3.2+ provides more granual cache invalidation data, should we report it instead of summing?
|
60
|
+
if RUBY_VERSION >= '3.2.0'
|
61
|
+
RubyVM.stat[:constant_cache].values.sum
|
62
|
+
# Ruby < 3.2 uses :global_constant_state
|
63
|
+
else
|
64
|
+
RubyVM.stat[:global_constant_state]
|
53
65
|
end
|
54
66
|
end
|
55
67
|
|