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.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +20 -0
  3. data/README.md +1 -13
  4. data/lib/new_relic/agent/agent.rb +40 -15
  5. data/lib/new_relic/agent/agent_logger.rb +1 -1
  6. data/lib/new_relic/agent/configuration/default_source.rb +16 -6
  7. data/lib/new_relic/agent/configuration/server_source.rb +2 -1
  8. data/lib/new_relic/agent/error_collector.rb +13 -21
  9. data/lib/new_relic/agent/error_event_aggregator.rb +124 -0
  10. data/lib/new_relic/agent/error_trace_aggregator.rb +15 -5
  11. data/lib/new_relic/agent/instrumentation/middleware_tracing.rb +16 -2
  12. data/lib/new_relic/agent/new_relic_service.rb +11 -10
  13. data/lib/new_relic/agent/new_relic_service/json_marshaller.rb +1 -1
  14. data/lib/new_relic/agent/payload_metric_mapping.rb +58 -0
  15. data/lib/new_relic/agent/sampled_buffer.rb +15 -1
  16. data/lib/new_relic/agent/supported_versions.rb +4 -4
  17. data/lib/new_relic/agent/transaction.rb +52 -44
  18. data/lib/new_relic/agent/transaction/request_attributes.rb +110 -0
  19. data/lib/new_relic/agent/transaction_event_aggregator.rb +4 -41
  20. data/lib/new_relic/noticed_error.rb +25 -9
  21. data/lib/new_relic/rack/agent_middleware.rb +5 -0
  22. data/lib/new_relic/version.rb +2 -2
  23. data/newrelic_rpm.gemspec +6 -0
  24. data/test/agent_helper.rb +16 -7
  25. data/test/environments/norails/Gemfile +1 -0
  26. data/test/multiverse/lib/multiverse/runner.rb +1 -1
  27. data/test/multiverse/lib/multiverse/suite.rb +6 -3
  28. data/test/multiverse/suites/agent_only/agent_attributes_test.rb +57 -0
  29. data/test/multiverse/suites/agent_only/audit_log_test.rb +1 -2
  30. data/test/multiverse/suites/agent_only/error_events_test.rb +82 -0
  31. data/test/multiverse/suites/agent_only/harvest_timestamps_test.rb +31 -6
  32. data/test/multiverse/suites/agent_only/logging_test.rb +2 -2
  33. data/test/multiverse/suites/agent_only/marshaling_test.rb +6 -0
  34. data/test/multiverse/suites/agent_only/set_transaction_name_test.rb +2 -2
  35. data/test/multiverse/suites/agent_only/thread_profiling_test.rb +0 -20
  36. data/test/multiverse/suites/grape/grape_test.rb +16 -1
  37. data/test/multiverse/suites/no_json/Envfile +12 -0
  38. data/test/multiverse/suites/no_json/config/newrelic.yml +27 -0
  39. data/test/multiverse/suites/no_json/marshal_config_test.rb +22 -0
  40. data/test/multiverse/suites/rack/example_app.rb +19 -0
  41. data/test/multiverse/suites/rack/response_content_type_test.rb +50 -0
  42. data/test/multiverse/suites/rails/error_tracing_test.rb +12 -7
  43. data/test/multiverse/suites/rails/parameter_capture_test.rb +21 -1
  44. data/test/multiverse/suites/sinatra/sinatra_error_tracing_test.rb +5 -2
  45. data/test/multiverse/suites/sinatra/sinatra_parameter_capture_test.rb +16 -1
  46. data/test/multiverse/suites/sinatra/sinatra_test_cases.rb +4 -2
  47. data/test/new_relic/agent/agent_logger_test.rb +9 -0
  48. data/test/new_relic/agent/agent_test.rb +6 -4
  49. data/test/new_relic/agent/audit_logger_test.rb +0 -7
  50. data/test/new_relic/agent/error_collector_test.rb +20 -250
  51. data/test/new_relic/agent/error_event_aggregator_test.rb +294 -0
  52. data/test/new_relic/agent/error_trace_aggregator_test.rb +273 -5
  53. data/test/new_relic/agent/instrumentation/task_instrumentation_test.rb +2 -2
  54. data/test/new_relic/agent/new_relic_service_test.rb +8 -32
  55. data/test/new_relic/agent/payload_metric_mapping_test.rb +74 -0
  56. data/test/new_relic/agent/pipe_channel_manager_test.rb +6 -5
  57. data/test/new_relic/agent/sampled_buffer_test.rb +36 -0
  58. data/test/new_relic/agent/sql_sampler_test.rb +4 -14
  59. data/test/new_relic/agent/stats_engine/stats_hash_test.rb +5 -4
  60. data/test/new_relic/agent/threading/thread_profile_test.rb +1 -2
  61. data/test/new_relic/agent/transaction/request_attributes_test.rb +76 -0
  62. data/test/new_relic/agent/transaction_event_aggregator_test.rb +12 -1
  63. data/test/new_relic/agent/transaction_test.rb +60 -11
  64. data/test/new_relic/data_container_tests.rb +17 -6
  65. data/test/new_relic/fake_collector.rb +16 -21
  66. data/test/new_relic/marshalling_test_cases.rb +1 -0
  67. data/test/new_relic/noticed_error_test.rb +59 -0
  68. data/test/new_relic/rack/agent_hooks_test.rb +1 -1
  69. data/test/new_relic/rack/error_collector_test.rb +7 -5
  70. data/test/performance/suites/error_collector.rb +28 -0
  71. data/test/performance/suites/marshalling.rb +0 -8
  72. metadata +14 -3
  73. 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
- ::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}") if over_limit
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
- capture_http_response_code(state, result)
70
- events.notify(:after_call, env, result) if first_middleware
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
- begin
60
- if marshaller == 'json'
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.warn "The JSON marshaller in use (#{NewRelic::JSONWrapper.backend_name}) is not recommended. Ensure the 'json' gem is available in your application for better performance."
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 append_event(x)
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
- "Last supported agent on 1.8.6 was 3.6.8.168"]
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
- :request_path,
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
- @request_path = path_from_request(request)
291
- @referer = referer_from_request(request)
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(state, start_time, end_time)
530
+ send_transaction_finished_event
514
531
  end
515
532
 
516
533
  def assign_agent_attributes
517
- if referer
518
- add_agent_attribute(:'request.headers.referer', referer,
519
- NewRelic::Agent::AttributeFilter::DST_ERROR_COLLECTOR)
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
- NewRelic::Agent::AttributeFilter::DST_TRANSACTION_TRACER|
525
- NewRelic::Agent::AttributeFilter::DST_TRANSACTION_EVENTS|
526
- NewRelic::Agent::AttributeFilter::DST_ERROR_COLLECTOR)
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(state, start_time, end_time)
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