newrelic_rpm 3.13.2.302 → 3.14.0.305
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +20 -0
- data/README.md +1 -13
- data/lib/new_relic/agent/agent.rb +40 -15
- data/lib/new_relic/agent/agent_logger.rb +1 -1
- data/lib/new_relic/agent/configuration/default_source.rb +16 -6
- data/lib/new_relic/agent/configuration/server_source.rb +2 -1
- data/lib/new_relic/agent/error_collector.rb +13 -21
- data/lib/new_relic/agent/error_event_aggregator.rb +124 -0
- data/lib/new_relic/agent/error_trace_aggregator.rb +15 -5
- data/lib/new_relic/agent/instrumentation/middleware_tracing.rb +16 -2
- data/lib/new_relic/agent/new_relic_service.rb +11 -10
- data/lib/new_relic/agent/new_relic_service/json_marshaller.rb +1 -1
- data/lib/new_relic/agent/payload_metric_mapping.rb +58 -0
- data/lib/new_relic/agent/sampled_buffer.rb +15 -1
- data/lib/new_relic/agent/supported_versions.rb +4 -4
- data/lib/new_relic/agent/transaction.rb +52 -44
- data/lib/new_relic/agent/transaction/request_attributes.rb +110 -0
- data/lib/new_relic/agent/transaction_event_aggregator.rb +4 -41
- data/lib/new_relic/noticed_error.rb +25 -9
- data/lib/new_relic/rack/agent_middleware.rb +5 -0
- data/lib/new_relic/version.rb +2 -2
- data/newrelic_rpm.gemspec +6 -0
- data/test/agent_helper.rb +16 -7
- data/test/environments/norails/Gemfile +1 -0
- data/test/multiverse/lib/multiverse/runner.rb +1 -1
- data/test/multiverse/lib/multiverse/suite.rb +6 -3
- data/test/multiverse/suites/agent_only/agent_attributes_test.rb +57 -0
- data/test/multiverse/suites/agent_only/audit_log_test.rb +1 -2
- data/test/multiverse/suites/agent_only/error_events_test.rb +82 -0
- data/test/multiverse/suites/agent_only/harvest_timestamps_test.rb +31 -6
- data/test/multiverse/suites/agent_only/logging_test.rb +2 -2
- data/test/multiverse/suites/agent_only/marshaling_test.rb +6 -0
- data/test/multiverse/suites/agent_only/set_transaction_name_test.rb +2 -2
- data/test/multiverse/suites/agent_only/thread_profiling_test.rb +0 -20
- data/test/multiverse/suites/grape/grape_test.rb +16 -1
- data/test/multiverse/suites/no_json/Envfile +12 -0
- data/test/multiverse/suites/no_json/config/newrelic.yml +27 -0
- data/test/multiverse/suites/no_json/marshal_config_test.rb +22 -0
- data/test/multiverse/suites/rack/example_app.rb +19 -0
- data/test/multiverse/suites/rack/response_content_type_test.rb +50 -0
- data/test/multiverse/suites/rails/error_tracing_test.rb +12 -7
- data/test/multiverse/suites/rails/parameter_capture_test.rb +21 -1
- data/test/multiverse/suites/sinatra/sinatra_error_tracing_test.rb +5 -2
- data/test/multiverse/suites/sinatra/sinatra_parameter_capture_test.rb +16 -1
- data/test/multiverse/suites/sinatra/sinatra_test_cases.rb +4 -2
- data/test/new_relic/agent/agent_logger_test.rb +9 -0
- data/test/new_relic/agent/agent_test.rb +6 -4
- data/test/new_relic/agent/audit_logger_test.rb +0 -7
- data/test/new_relic/agent/error_collector_test.rb +20 -250
- data/test/new_relic/agent/error_event_aggregator_test.rb +294 -0
- data/test/new_relic/agent/error_trace_aggregator_test.rb +273 -5
- data/test/new_relic/agent/instrumentation/task_instrumentation_test.rb +2 -2
- data/test/new_relic/agent/new_relic_service_test.rb +8 -32
- data/test/new_relic/agent/payload_metric_mapping_test.rb +74 -0
- data/test/new_relic/agent/pipe_channel_manager_test.rb +6 -5
- data/test/new_relic/agent/sampled_buffer_test.rb +36 -0
- data/test/new_relic/agent/sql_sampler_test.rb +4 -14
- data/test/new_relic/agent/stats_engine/stats_hash_test.rb +5 -4
- data/test/new_relic/agent/threading/thread_profile_test.rb +1 -2
- data/test/new_relic/agent/transaction/request_attributes_test.rb +76 -0
- data/test/new_relic/agent/transaction_event_aggregator_test.rb +12 -1
- data/test/new_relic/agent/transaction_test.rb +60 -11
- data/test/new_relic/data_container_tests.rb +17 -6
- data/test/new_relic/fake_collector.rb +16 -21
- data/test/new_relic/marshalling_test_cases.rb +1 -0
- data/test/new_relic/noticed_error_test.rb +59 -0
- data/test/new_relic/rack/agent_hooks_test.rb +1 -1
- data/test/new_relic/rack/error_collector_test.rb +7 -5
- data/test/performance/suites/error_collector.rb +28 -0
- data/test/performance/suites/marshalling.rb +0 -8
- metadata +14 -3
- data/lib/new_relic/agent/new_relic_service/pruby_marshaller.rb +0 -56
@@ -10,6 +10,11 @@ module NewRelic
|
|
10
10
|
@capacity = capacity
|
11
11
|
@lock = Mutex.new
|
12
12
|
@errors = []
|
13
|
+
register_config_callbacks
|
14
|
+
end
|
15
|
+
|
16
|
+
def enabled?
|
17
|
+
Agent.config[:'error_collector.enabled']
|
13
18
|
end
|
14
19
|
|
15
20
|
def merge!(errors)
|
@@ -38,6 +43,7 @@ module NewRelic
|
|
38
43
|
# the error queue is too long - if so, we drop the error on the
|
39
44
|
# floor after logging a warning.
|
40
45
|
def add_to_error_queue(noticed_error)
|
46
|
+
return unless enabled?
|
41
47
|
@lock.synchronize do
|
42
48
|
if !over_queue_limit?(noticed_error.message) && !@errors.include?(noticed_error)
|
43
49
|
@errors << noticed_error
|
@@ -49,14 +55,12 @@ module NewRelic
|
|
49
55
|
# the maximum limit, and logs a warning if we are over the limit.
|
50
56
|
def over_queue_limit?(message)
|
51
57
|
over_limit = (@errors.reject{|err| err.is_internal}.length >= @capacity)
|
52
|
-
|
58
|
+
if over_limit
|
59
|
+
::NewRelic::Agent.logger.warn("The error reporting queue has reached #{@capacity}. The error detail for this and subsequent errors will not be transmitted to New Relic until the queued errors have been sent: #{message}")
|
60
|
+
end
|
53
61
|
over_limit
|
54
62
|
end
|
55
63
|
|
56
|
-
def errors
|
57
|
-
@lock.synchronize{ @errors }
|
58
|
-
end
|
59
|
-
|
60
64
|
# *Use sparingly for difficult to track bugs.*
|
61
65
|
#
|
62
66
|
# Track internal agent errors for communication back to New Relic.
|
@@ -84,6 +88,12 @@ module NewRelic
|
|
84
88
|
rescue => e
|
85
89
|
NewRelic::Agent.logger.info("Unable to capture internal agent error due to an exception:", e)
|
86
90
|
end
|
91
|
+
|
92
|
+
def register_config_callbacks
|
93
|
+
Agent.config.register_callback(:'error_collector.enabled') do |enabled|
|
94
|
+
::NewRelic::Agent.logger.debug "Error traces will #{enabled ? '' : 'not '}be sent to the New Relic service."
|
95
|
+
end
|
96
|
+
end
|
87
97
|
end
|
88
98
|
end
|
89
99
|
end
|
@@ -55,6 +55,17 @@ module NewRelic
|
|
55
55
|
end
|
56
56
|
end
|
57
57
|
|
58
|
+
# the trailing unless is for the benefit for Ruby 1.8.7 and can be removed
|
59
|
+
# when it is deprecated.
|
60
|
+
CONTENT_TYPE = 'Content-Type'.freeze unless defined?(CONTENT_TYPE)
|
61
|
+
|
62
|
+
def capture_response_content_type(state, result)
|
63
|
+
if result.is_a?(Array) && state.current_transaction
|
64
|
+
_, headers, _ = result
|
65
|
+
state.current_transaction.response_content_type = headers[CONTENT_TYPE]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
58
69
|
def call(env)
|
59
70
|
first_middleware = note_transaction_started(env)
|
60
71
|
|
@@ -66,8 +77,11 @@ module NewRelic
|
|
66
77
|
|
67
78
|
result = (target == self) ? traced_call(env) : target.call(env)
|
68
79
|
|
69
|
-
|
70
|
-
|
80
|
+
if first_middleware
|
81
|
+
capture_http_response_code(state, result)
|
82
|
+
capture_response_content_type(state, result)
|
83
|
+
events.notify(:after_call, env, result)
|
84
|
+
end
|
71
85
|
|
72
86
|
result
|
73
87
|
rescue Exception => e
|
@@ -7,7 +7,6 @@ require 'new_relic/agent/audit_logger'
|
|
7
7
|
require 'new_relic/agent/new_relic_service/encoders'
|
8
8
|
require 'new_relic/agent/new_relic_service/marshaller'
|
9
9
|
require 'new_relic/agent/new_relic_service/json_marshaller'
|
10
|
-
require 'new_relic/agent/new_relic_service/pruby_marshaller'
|
11
10
|
|
12
11
|
module NewRelic
|
13
12
|
module Agent
|
@@ -56,16 +55,11 @@ module NewRelic
|
|
56
55
|
end
|
57
56
|
|
58
57
|
Agent.config.register_callback(:marshaller) do |marshaller|
|
59
|
-
|
60
|
-
|
61
|
-
@marshaller = JsonMarshaller.new
|
62
|
-
else
|
63
|
-
@marshaller = PrubyMarshaller.new
|
64
|
-
end
|
65
|
-
rescue LoadError
|
66
|
-
::NewRelic::Agent.logger.warn("JSON marshaller requested, but the 'json' gem was not available, falling back to pruby. This will not be supported in future versions of the agent.")
|
67
|
-
@marshaller = PrubyMarshaller.new
|
58
|
+
if marshaller != 'json'
|
59
|
+
::NewRelic::Agent.logger.warn("Non-JSON marshaller '#{marshaller}' requested but not supported, using JSON marshaller instead. pruby marshalling has been removed as of version 3.14.0.")
|
68
60
|
end
|
61
|
+
|
62
|
+
@marshaller = JsonMarshaller.new
|
69
63
|
end
|
70
64
|
end
|
71
65
|
|
@@ -185,6 +179,13 @@ module NewRelic
|
|
185
179
|
:item_count => data.size)
|
186
180
|
end
|
187
181
|
|
182
|
+
def error_event_data(data)
|
183
|
+
metadata, items = data
|
184
|
+
invoke_remote(:error_event_data, [@agent_id, *data], :item_count => items.size)
|
185
|
+
NewRelic::Agent.record_metric("Supportability/Events/TransactionError/Sent", :count => items.size)
|
186
|
+
NewRelic::Agent.record_metric("Supportability/Events/TransactionError/Seen", :count => metadata[:events_seen])
|
187
|
+
end
|
188
|
+
|
188
189
|
# We do not compress if content is smaller than 64kb. There are
|
189
190
|
# problems with bugs in Ruby in some versions that expose us
|
190
191
|
# to a risk of segfaults if we compress aggressively.
|
@@ -12,7 +12,7 @@ module NewRelic
|
|
12
12
|
def initialize
|
13
13
|
::NewRelic::Agent.logger.debug "Using JSON marshaller (#{NewRelic::JSONWrapper.backend_name})"
|
14
14
|
unless self.class.is_supported?
|
15
|
-
::NewRelic::Agent.logger.
|
15
|
+
::NewRelic::Agent.logger.error "JSON backend #{NewRelic::JSONWrapper.backend_name} is not supported."
|
16
16
|
end
|
17
17
|
warn_for_yajl
|
18
18
|
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
# encoding: utf-8
|
3
|
+
# This file is distributed under New Relic's license terms.
|
4
|
+
# See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details.
|
5
|
+
|
6
|
+
module NewRelic
|
7
|
+
module Agent
|
8
|
+
module PayloadMetricMapping
|
9
|
+
|
10
|
+
# this logic was extracted from TransactionEventAggregator for reuse by
|
11
|
+
# the ErrorEventAggregator
|
12
|
+
|
13
|
+
SPEC_MAPPINGS = {}
|
14
|
+
|
15
|
+
class << self
|
16
|
+
def append_mapped_metrics(txn_metrics, sample)
|
17
|
+
if txn_metrics
|
18
|
+
SPEC_MAPPINGS.each do |(name, extracted_values)|
|
19
|
+
if txn_metrics.has_key?(name)
|
20
|
+
stat = txn_metrics[name]
|
21
|
+
extracted_values.each do |value_name, key_name|
|
22
|
+
sample[key_name] = stat.send(value_name)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def map_metric(metric_name, to_add={})
|
32
|
+
to_add.values.each(&:freeze)
|
33
|
+
|
34
|
+
mappings = SPEC_MAPPINGS.fetch(metric_name, {})
|
35
|
+
mappings.merge!(to_add)
|
36
|
+
|
37
|
+
SPEC_MAPPINGS[metric_name] = mappings
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# All Transactions
|
42
|
+
# Don't need to use the transaction-type specific metrics since this is
|
43
|
+
# scoped to just one transaction, so Datastore/all has what we want.
|
44
|
+
map_metric('Datastore/all', :total_call_time => 'databaseDuration')
|
45
|
+
map_metric('Datastore/all', :call_count => 'databaseCallCount')
|
46
|
+
map_metric('GC/Transaction/all', :total_call_time => 'gcCumulative')
|
47
|
+
|
48
|
+
# Web Metrics
|
49
|
+
map_metric('WebFrontend/QueueTime', :total_call_time => 'queueDuration')
|
50
|
+
map_metric('External/allWeb', :total_call_time => 'externalDuration')
|
51
|
+
map_metric('External/allWeb', :call_count => 'externalCallCount')
|
52
|
+
|
53
|
+
# Background Metrics
|
54
|
+
map_metric('External/allOther', :total_call_time => 'externalDuration')
|
55
|
+
map_metric('External/allOther', :call_count => 'externalCallCount')
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -26,13 +26,22 @@ module NewRelic
|
|
26
26
|
super
|
27
27
|
end
|
28
28
|
|
29
|
-
def
|
29
|
+
def append(x = nil, &blk)
|
30
|
+
@seen += 1
|
31
|
+
append_event(x, &blk)
|
32
|
+
end
|
33
|
+
|
34
|
+
def append_event(x = nil, &blk)
|
35
|
+
raise ArgumentError, "Expected argument or block, but received both" if x && blk
|
36
|
+
|
30
37
|
if @items.size < @capacity
|
38
|
+
x = blk.call if block_given?
|
31
39
|
@items << x
|
32
40
|
return x
|
33
41
|
else
|
34
42
|
m = rand(@seen) # [0, @seen)
|
35
43
|
if m < @capacity
|
44
|
+
x = blk.call if block_given?
|
36
45
|
@items[m] = x
|
37
46
|
return x
|
38
47
|
else
|
@@ -42,6 +51,11 @@ module NewRelic
|
|
42
51
|
end
|
43
52
|
end
|
44
53
|
|
54
|
+
def decrement_lifetime_counts_by n
|
55
|
+
@captured_lifetime -= n
|
56
|
+
@seen_lifetime -= n
|
57
|
+
end
|
58
|
+
|
45
59
|
def sample_rate_lifetime
|
46
60
|
@captured_lifetime > 0 ? (@captured_lifetime.to_f / @seen_lifetime) : 0.0
|
47
61
|
end
|
@@ -12,18 +12,18 @@ module NewRelic
|
|
12
12
|
:type => :ruby,
|
13
13
|
:name => "MRI",
|
14
14
|
:supported => ["1.8.7", "1.9.2", "1.9.3", "2.0.0", "~> 2.1.0", "~> 2.2.0" ],
|
15
|
-
:deprecated => ["1.8.6"],
|
16
15
|
:url => "https://www.ruby-lang.org",
|
17
16
|
:feed => "https://www.ruby-lang.org/en/feeds/news.rss",
|
18
17
|
:notes => [
|
19
|
-
"1.8.7 includes support for Ruby Enterprise Edition (REE)",
|
20
|
-
"
|
18
|
+
"1.8.7 includes support for Ruby Enterprise Edition (REE).",
|
19
|
+
"1.8.7 & REE require the 'json' gem to be present in your Gemfile/operating environment.",
|
20
|
+
"Last supported agent on 1.8.6 was 3.6.8.168."]
|
21
21
|
},
|
22
22
|
:jruby =>
|
23
23
|
{
|
24
24
|
:type => :ruby,
|
25
25
|
:name => "JRuby",
|
26
|
-
:supported => ["~> 1.6.0", "~> 1.7.0"],
|
26
|
+
:supported => ["~> 1.6.0", "~> 1.7.0", "~> 9.0"],
|
27
27
|
:url => "http://jruby.org",
|
28
28
|
:feed => "http://jruby.org/atom.xml"
|
29
29
|
},
|
@@ -7,6 +7,7 @@ require 'new_relic/agent/instrumentation/queue_time'
|
|
7
7
|
require 'new_relic/agent/transaction_metrics'
|
8
8
|
require 'new_relic/agent/method_tracer_helpers'
|
9
9
|
require 'new_relic/agent/transaction/attributes'
|
10
|
+
require 'new_relic/agent/transaction/request_attributes'
|
10
11
|
|
11
12
|
module NewRelic
|
12
13
|
module Agent
|
@@ -51,7 +52,8 @@ module NewRelic
|
|
51
52
|
:filtered_params,
|
52
53
|
:jruby_cpu_start,
|
53
54
|
:process_cpu_start,
|
54
|
-
:http_response_code
|
55
|
+
:http_response_code,
|
56
|
+
:response_content_type
|
55
57
|
|
56
58
|
attr_reader :guid,
|
57
59
|
:metrics,
|
@@ -60,8 +62,7 @@ module NewRelic
|
|
60
62
|
:frame_stack,
|
61
63
|
:cat_path_hashes,
|
62
64
|
:attributes,
|
63
|
-
:
|
64
|
-
:referer
|
65
|
+
:payload
|
65
66
|
|
66
67
|
# Populated with the trace sample once this transaction is completed.
|
67
68
|
attr_reader :transaction_trace
|
@@ -282,16 +283,31 @@ module NewRelic
|
|
282
283
|
@ignore_enduser = false
|
283
284
|
@ignore_trace = false
|
284
285
|
|
286
|
+
@error_recorded = false
|
287
|
+
|
285
288
|
@attributes = Attributes.new(NewRelic::Agent.instance.attribute_filter)
|
286
289
|
|
287
290
|
merge_request_parameters(@filtered_params)
|
288
291
|
|
289
292
|
if request = options[:request]
|
290
|
-
@
|
291
|
-
|
293
|
+
@request_attributes = RequestAttributes.new request
|
294
|
+
else
|
295
|
+
@request_attributes = nil
|
292
296
|
end
|
293
297
|
end
|
294
298
|
|
299
|
+
def referer
|
300
|
+
@request_attributes && @request_attributes.referer
|
301
|
+
end
|
302
|
+
|
303
|
+
def request_path
|
304
|
+
@request_attributes && @request_attributes.request_path
|
305
|
+
end
|
306
|
+
|
307
|
+
def request_port
|
308
|
+
@request_attributes && @request_attributes.port
|
309
|
+
end
|
310
|
+
|
295
311
|
# This transaction-local hash may be used as temprory storage by
|
296
312
|
# instrumentation that needs to pass data from one instrumentation point
|
297
313
|
# to another.
|
@@ -507,23 +523,28 @@ module NewRelic
|
|
507
523
|
record_apdex(state, end_time) unless ignore_apdex?
|
508
524
|
record_queue_time
|
509
525
|
|
526
|
+
generate_payload(state, start_time, end_time)
|
510
527
|
record_exceptions
|
511
528
|
merge_metrics
|
512
529
|
|
513
|
-
send_transaction_finished_event
|
530
|
+
send_transaction_finished_event
|
514
531
|
end
|
515
532
|
|
516
533
|
def assign_agent_attributes
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
end
|
534
|
+
default_destinations = AttributeFilter::DST_TRANSACTION_TRACER |
|
535
|
+
AttributeFilter::DST_TRANSACTION_EVENTS |
|
536
|
+
AttributeFilter::DST_ERROR_COLLECTOR
|
521
537
|
|
522
538
|
if http_response_code
|
523
|
-
add_agent_attribute(:httpResponseCode, http_response_code.to_s,
|
524
|
-
|
525
|
-
|
526
|
-
|
539
|
+
add_agent_attribute(:httpResponseCode, http_response_code.to_s, default_destinations)
|
540
|
+
end
|
541
|
+
|
542
|
+
if response_content_type
|
543
|
+
add_agent_attribute(:'response.headers.contentType', response_content_type, default_destinations)
|
544
|
+
end
|
545
|
+
|
546
|
+
if @request_attributes
|
547
|
+
@request_attributes.assign_agent_attributes self
|
527
548
|
end
|
528
549
|
end
|
529
550
|
|
@@ -563,22 +584,25 @@ module NewRelic
|
|
563
584
|
|
564
585
|
# This event is fired when the transaction is fully completed. The metric
|
565
586
|
# values and sampler can't be successfully modified from this event.
|
566
|
-
def send_transaction_finished_event
|
587
|
+
def send_transaction_finished_event
|
588
|
+
agent.events.notify(:transaction_finished, payload)
|
589
|
+
end
|
590
|
+
|
591
|
+
def generate_payload(state, start_time, end_time)
|
567
592
|
duration = end_time.to_f - start_time.to_f
|
568
|
-
payload = {
|
593
|
+
@payload = {
|
569
594
|
:name => @frozen_name,
|
570
595
|
:bucket => recording_web_transaction? ? :request : :background,
|
571
596
|
:start_timestamp => start_time.to_f,
|
572
597
|
:duration => duration,
|
573
598
|
:metrics => @metrics,
|
574
599
|
:attributes => @attributes,
|
600
|
+
:error => error_recorded?
|
575
601
|
}
|
576
|
-
append_cat_info(state, duration, payload)
|
577
|
-
append_apdex_perf_zone(duration, payload)
|
578
|
-
append_synthetics_to(state, payload)
|
579
|
-
append_referring_transaction_guid_to(state, payload)
|
580
|
-
|
581
|
-
agent.events.notify(:transaction_finished, payload)
|
602
|
+
append_cat_info(state, duration, @payload)
|
603
|
+
append_apdex_perf_zone(duration, @payload)
|
604
|
+
append_synthetics_to(state, @payload)
|
605
|
+
append_referring_transaction_guid_to(state, @payload)
|
582
606
|
end
|
583
607
|
|
584
608
|
def include_guid?(state, duration)
|
@@ -703,10 +727,11 @@ module NewRelic
|
|
703
727
|
def record_exceptions
|
704
728
|
@exceptions.each do |exception, options|
|
705
729
|
options[:uri] ||= request_path if request_path
|
730
|
+
options[:port] = request_port if request_port
|
706
731
|
options[:metric] = best_name
|
707
732
|
options[:attributes] = @attributes
|
708
733
|
|
709
|
-
agent.error_collector.notice_error(exception, options)
|
734
|
+
@error_recorded = !!agent.error_collector.notice_error(exception, options) || @error_recorded
|
710
735
|
end
|
711
736
|
end
|
712
737
|
|
@@ -719,6 +744,10 @@ module NewRelic
|
|
719
744
|
end
|
720
745
|
end
|
721
746
|
|
747
|
+
def error_recorded?
|
748
|
+
@error_recorded
|
749
|
+
end
|
750
|
+
|
722
751
|
QUEUE_TIME_METRIC = 'WebFrontend/QueueTime'.freeze
|
723
752
|
|
724
753
|
def queue_time
|
@@ -916,27 +945,6 @@ module NewRelic
|
|
916
945
|
end
|
917
946
|
guid
|
918
947
|
end
|
919
|
-
|
920
|
-
# Make a safe attempt to get the referer from a request object, generally successful when
|
921
|
-
# it's a Rack request.
|
922
|
-
def referer_from_request(req)
|
923
|
-
if req && req.respond_to?(:referer) && req.referer
|
924
|
-
HTTPClients::URIUtil.strip_query_string(req.referer.to_s)
|
925
|
-
end
|
926
|
-
end
|
927
|
-
|
928
|
-
# In practice we expect req to be a Rack::Request or ActionController::AbstractRequest
|
929
|
-
# (for older Rails versions). But anything that responds to path can be passed to
|
930
|
-
# perform_action_with_newrelic_trace.
|
931
|
-
#
|
932
|
-
# We don't expect the path to include a query string, however older test helpers for
|
933
|
-
# rails construct the PATH_INFO enviroment variable improperly and we're generally
|
934
|
-
# being defensive.
|
935
|
-
def path_from_request(req)
|
936
|
-
path = req.path
|
937
|
-
path = HTTPClients::URIUtil.strip_query_string(path)
|
938
|
-
path.empty? ? "/" : path
|
939
|
-
end
|
940
948
|
end
|
941
949
|
end
|
942
950
|
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# This file is distributed under New Relic's license terms.
|
3
|
+
# See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details.
|
4
|
+
|
5
|
+
require 'new_relic/agent/http_clients/uri_util'
|
6
|
+
|
7
|
+
module NewRelic
|
8
|
+
module Agent
|
9
|
+
class Transaction
|
10
|
+
class RequestAttributes
|
11
|
+
attr_reader :request_path, :referer, :accept, :content_length, :host,
|
12
|
+
:port, :user_agent, :request_method
|
13
|
+
|
14
|
+
HTTP_ACCEPT_HEADER_KEY = "HTTP_ACCEPT".freeze
|
15
|
+
|
16
|
+
def initialize request
|
17
|
+
@request_path = path_from_request request
|
18
|
+
@referer = referer_from_request request
|
19
|
+
@accept = attribute_from_env request, HTTP_ACCEPT_HEADER_KEY
|
20
|
+
@content_length = content_length_from_request request
|
21
|
+
@host = attribute_from_request request, :host
|
22
|
+
@port = port_from_request request
|
23
|
+
@user_agent = attribute_from_request request, :user_agent
|
24
|
+
@request_method = attribute_from_request request, :request_method
|
25
|
+
end
|
26
|
+
|
27
|
+
def assign_agent_attributes txn
|
28
|
+
default_destinations = AttributeFilter::DST_TRANSACTION_TRACER|
|
29
|
+
AttributeFilter::DST_TRANSACTION_EVENTS|
|
30
|
+
AttributeFilter::DST_ERROR_COLLECTOR
|
31
|
+
|
32
|
+
if referer
|
33
|
+
txn.add_agent_attribute :'request.headers.referer', referer, AttributeFilter::DST_ERROR_COLLECTOR
|
34
|
+
end
|
35
|
+
|
36
|
+
if accept
|
37
|
+
txn.add_agent_attribute :'request.headers.accept', accept, default_destinations
|
38
|
+
end
|
39
|
+
|
40
|
+
if content_length
|
41
|
+
txn.add_agent_attribute :'request.headers.contentLength', content_length, default_destinations
|
42
|
+
end
|
43
|
+
|
44
|
+
if host
|
45
|
+
txn.add_agent_attribute :'request.headers.host', host, default_destinations
|
46
|
+
end
|
47
|
+
|
48
|
+
if user_agent
|
49
|
+
txn.add_agent_attribute :'request.headers.userAgent', user_agent, default_destinations
|
50
|
+
end
|
51
|
+
|
52
|
+
if request_method
|
53
|
+
txn.add_agent_attribute :'request.method', request_method, default_destinations
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
# Make a safe attempt to get the referer from a request object, generally successful when
|
60
|
+
# it's a Rack request.
|
61
|
+
|
62
|
+
def referer_from_request request
|
63
|
+
if referer = attribute_from_request(request, :referer)
|
64
|
+
HTTPClients::URIUtil.strip_query_string referer.to_s
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# In practice we expect req to be a Rack::Request or ActionController::AbstractRequest
|
69
|
+
# (for older Rails versions). But anything that responds to path can be passed to
|
70
|
+
# perform_action_with_newrelic_trace.
|
71
|
+
#
|
72
|
+
# We don't expect the path to include a query string, however older test helpers for
|
73
|
+
# rails construct the PATH_INFO enviroment variable improperly and we're generally
|
74
|
+
# being defensive.
|
75
|
+
|
76
|
+
ROOT_PATH = "/".freeze
|
77
|
+
|
78
|
+
def path_from_request request
|
79
|
+
path = attribute_from_request(request, :path) || ''
|
80
|
+
path = HTTPClients::URIUtil.strip_query_string(path)
|
81
|
+
path.empty? ? ROOT_PATH : path
|
82
|
+
end
|
83
|
+
|
84
|
+
def content_length_from_request request
|
85
|
+
if content_length = attribute_from_request(request, :content_length)
|
86
|
+
content_length.to_i
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def port_from_request request
|
91
|
+
if port = attribute_from_request(request, :port)
|
92
|
+
port.to_i
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def attribute_from_request request, attribute_method
|
97
|
+
if request.respond_to? attribute_method
|
98
|
+
request.send(attribute_method)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def attribute_from_env request, key
|
103
|
+
if env = attribute_from_request(request, :env)
|
104
|
+
env[key]
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|