newrelic_rpm 4.5.0.337 → 4.6.0.338

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +2 -0
  3. data/CHANGELOG.md +8 -0
  4. data/lib/new_relic/agent.rb +1 -0
  5. data/lib/new_relic/agent/agent.rb +19 -12
  6. data/lib/new_relic/agent/configuration/default_source.rb +7 -0
  7. data/lib/new_relic/agent/distributed_trace_monitor.rb +29 -0
  8. data/lib/new_relic/agent/distributed_trace_payload.rb +223 -0
  9. data/lib/new_relic/agent/distributed_trace_priority_sampled_buffer.rb +72 -0
  10. data/lib/new_relic/agent/external.rb +137 -0
  11. data/lib/new_relic/agent/http_clients/abstract_request.rb +31 -0
  12. data/lib/new_relic/agent/http_clients/curb_wrappers.rb +3 -1
  13. data/lib/new_relic/agent/http_clients/excon_wrappers.rb +3 -1
  14. data/lib/new_relic/agent/http_clients/http_rb_wrappers.rb +3 -1
  15. data/lib/new_relic/agent/http_clients/httpclient_wrappers.rb +3 -1
  16. data/lib/new_relic/agent/http_clients/net_http_wrappers.rb +3 -1
  17. data/lib/new_relic/agent/http_clients/typhoeus_wrappers.rb +3 -1
  18. data/lib/new_relic/agent/sql_sampler.rb +2 -2
  19. data/lib/new_relic/agent/throughput_monitor.rb +59 -0
  20. data/lib/new_relic/agent/transaction.rb +59 -15
  21. data/lib/new_relic/agent/transaction/abstract_segment.rb +17 -5
  22. data/lib/new_relic/agent/transaction/distributed_tracing.rb +121 -0
  23. data/lib/new_relic/agent/transaction/external_request_segment.rb +183 -19
  24. data/lib/new_relic/agent/transaction/segment.rb +2 -3
  25. data/lib/new_relic/agent/transaction/trace.rb +14 -5
  26. data/lib/new_relic/agent/transaction/trace_builder.rb +58 -0
  27. data/lib/new_relic/agent/transaction/trace_node.rb +34 -32
  28. data/lib/new_relic/agent/transaction/tracing.rb +4 -9
  29. data/lib/new_relic/agent/transaction_error_primitive.rb +18 -0
  30. data/lib/new_relic/agent/transaction_event_aggregator.rb +14 -0
  31. data/lib/new_relic/agent/transaction_event_primitive.rb +14 -0
  32. data/lib/new_relic/agent/transaction_event_recorder.rb +9 -1
  33. data/lib/new_relic/agent/transaction_sampler.rb +12 -66
  34. data/lib/new_relic/agent/transaction_state.rb +1 -3
  35. data/lib/new_relic/supportability_helper.rb +5 -0
  36. data/lib/new_relic/version.rb +1 -1
  37. data/newrelic.yml +1 -1
  38. data/newrelic_rpm.gemspec +1 -1
  39. data/test/agent_helper.rb +11 -3
  40. metadata +10 -4
  41. data/lib/new_relic/agent/transaction_sample_builder.rb +0 -138
@@ -0,0 +1,137 @@
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/transaction/tracing'
6
+ require 'new_relic/agent/cross_app_tracing'
7
+
8
+ module NewRelic
9
+ module Agent
10
+ module External
11
+ extend self
12
+
13
+ # This method creates and starts an external request segment using the
14
+ # given library, URI, and procedure. This is used to time external calls
15
+ # made over HTTP.
16
+ #
17
+ # @param [String] library a string of the class name of the library used to
18
+ # make the external call, for example, 'Net::HTTP'.
19
+ #
20
+ # @param [String, URI] uri indicates the URI to which the
21
+ # external request is being made. The URI should begin with the protocol,
22
+ # for example, 'https://github.com'.
23
+ #
24
+ # @param [String] procedure the HTTP method being used for the external
25
+ # request as a string, for example, 'GET'.
26
+ #
27
+ # @api public
28
+ def start_segment(library: nil, uri: nil, procedure: nil)
29
+ raise ArgumentError, 'Argument `library` is required' if library.nil?
30
+ raise ArgumentError, 'Argument `uri` is required' if uri.nil?
31
+ raise ArgumentError, 'Argument `procedure` is required' if procedure.nil?
32
+
33
+ ::NewRelic::Agent.record_api_supportability_metric(:start_segment)
34
+
35
+ ::NewRelic::Agent::Transaction.start_external_request_segment(library, uri, procedure)
36
+ end
37
+
38
+ NON_HTTP_CAT_ID_HEADER = 'NewRelicID'.freeze
39
+ NON_HTTP_CAT_TXN_HEADER = 'NewRelicTransaction'.freeze
40
+ NON_HTTP_CAT_SYNTHETICS_HEADER = 'NewRelicSynthetics'.freeze
41
+
42
+ # Process obfuscated +String+ indentifying a calling application and transaction that is also running a
43
+ # New Relic agent and save information in current transaction for inclusion in a trace. The +String+ is
44
+ # generated by +get_request_metadata+ on the calling application.
45
+ #
46
+ # @param request_metadata [String] received obfuscated request metadata
47
+ #
48
+ # @api public
49
+ #
50
+ def process_request_metadata request_metadata
51
+ NewRelic::Agent.record_api_supportability_metric(:process_request_metadata)
52
+ return unless CrossAppTracing.cross_app_enabled?
53
+
54
+ state = NewRelic::Agent::TransactionState.tl_get
55
+ if transaction = state.current_transaction
56
+ rmd = ::JSON.parse obfuscator.deobfuscate(request_metadata)
57
+
58
+ # handle/check ID
59
+ #
60
+ if id = rmd[NON_HTTP_CAT_ID_HEADER] and CrossAppTracing.trusted_valid_cross_app_id?(id)
61
+ state.client_cross_app_id = id
62
+
63
+ # handle transaction info
64
+ #
65
+ if txn_info = rmd[NON_HTTP_CAT_TXN_HEADER]
66
+ state.referring_transaction_info = txn_info
67
+ CrossAppTracing.assign_intrinsic_transaction_attributes state
68
+ end
69
+
70
+ # handle synthetics
71
+ #
72
+ if synth = rmd[NON_HTTP_CAT_SYNTHETICS_HEADER]
73
+ transaction.synthetics_payload = synth
74
+ transaction.raw_synthetics_header = obfuscator.obfuscate ::JSON.dump(synth)
75
+ end
76
+
77
+ else
78
+ NewRelic::Agent.logger.error "error processing request metadata: invalid/non-trusted ID: '#{id}'"
79
+ end
80
+
81
+ nil
82
+ end
83
+ rescue => e
84
+ NewRelic::Agent.logger.error 'error during process_request_metadata', e
85
+ end
86
+
87
+ # Obtain an obfuscated +String+ suitable for delivery across public networks that carries transaction
88
+ # information from this application to a calling application which is also running a New Relic agent.
89
+ # This +String+ can be processed by +process_response_metadata+ on the calling application.
90
+ #
91
+ # @return [String] obfuscated response metadata to send
92
+ #
93
+ # @api public
94
+ #
95
+ def get_response_metadata
96
+ NewRelic::Agent.record_api_supportability_metric(:get_response_metadata)
97
+ return unless CrossAppTracing.cross_app_enabled?
98
+
99
+ state = NewRelic::Agent::TransactionState.tl_get
100
+ if transaction = state.current_transaction and state.client_cross_app_id
101
+
102
+ # must freeze the name since we're responding with it
103
+ #
104
+ transaction.freeze_name_and_execute_if_not_ignored do
105
+
106
+ # build response payload
107
+ #
108
+ rmd = {
109
+ NewRelicAppData: [
110
+ NewRelic::Agent.config[:cross_process_id],
111
+ state.timings.transaction_name,
112
+ state.timings.queue_time_in_seconds.to_f,
113
+ state.timings.app_time_in_seconds.to_f,
114
+ -1, # per non-HTTP CAT spec
115
+ state.request_guid
116
+ ]
117
+ }
118
+
119
+ # obfuscate the generated response metadata JSON
120
+ #
121
+ obfuscator.obfuscate ::JSON.dump(rmd)
122
+
123
+ end
124
+ end
125
+ rescue => e
126
+ NewRelic::Agent.logger.error "error during get_response_metadata", e
127
+ end
128
+
129
+ private
130
+
131
+ def obfuscator
132
+ CrossAppTracing.obfuscator
133
+ end
134
+
135
+ end
136
+ end
137
+ end
@@ -0,0 +1,31 @@
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
+ module NewRelic
6
+ module Agent
7
+ module HTTPClients
8
+
9
+ # This class provides a public interface for wrapping HTTP requests. This
10
+ # may be used to create wrappers that are compatible with New Relic's
11
+ # external request API.
12
+ #
13
+ # @api public
14
+ class AbstractRequest
15
+ ERROR_MESSAGE = 'Subclasses of NewRelic::Agent::HTTPClients::AbstractRequest must implement a '.freeze
16
+
17
+ def []
18
+ raise NotImplementedError, ERROR_MESSAGE + ':[] method'
19
+ end
20
+
21
+ def []=
22
+ raise NotImplementedError, ERROR_MESSAGE + ':[]= method'
23
+ end
24
+
25
+ def host_from_header
26
+ raise NotImplementedError, ERROR_MESSAGE + ':host_from_header method'
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -2,6 +2,8 @@
2
2
  # This file is distributed under New Relic's license terms.
3
3
  # See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details.
4
4
 
5
+ require 'new_relic/agent/http_clients/abstract_request'
6
+
5
7
  module NewRelic
6
8
  module Agent
7
9
  module HTTPClients
@@ -45,7 +47,7 @@ module NewRelic
45
47
  end
46
48
 
47
49
 
48
- class CurbResponse
50
+ class CurbResponse < AbstractRequest
49
51
 
50
52
  def initialize(curlobj)
51
53
  @headers = {}
@@ -2,6 +2,8 @@
2
2
  # This file is distributed under New Relic's license terms.
3
3
  # See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details.
4
4
 
5
+ require 'new_relic/agent/http_clients/abstract_request'
6
+
5
7
  module NewRelic
6
8
  module Agent
7
9
  module HTTPClients
@@ -26,7 +28,7 @@ module NewRelic
26
28
  end
27
29
  end
28
30
 
29
- class ExconHTTPRequest
31
+ class ExconHTTPRequest < AbstractRequest
30
32
  attr_reader :method
31
33
 
32
34
  EXCON = "Excon".freeze
@@ -2,6 +2,8 @@
2
2
  # This file is distributed under New Relic's license terms.
3
3
  # See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details.
4
4
 
5
+ require 'new_relic/agent/http_clients/abstract_request'
6
+
5
7
  module NewRelic
6
8
  module Agent
7
9
  module HTTPClients
@@ -22,7 +24,7 @@ module NewRelic
22
24
  end
23
25
  end
24
26
 
25
- class HTTPRequest
27
+ class HTTPRequest < AbstractRequest
26
28
  HTTP_RB = 'http.rb'.freeze
27
29
  HOST = 'host'.freeze
28
30
  COLON = ':'.freeze
@@ -2,6 +2,8 @@
2
2
  # This file is distributed under New Relic's license terms.
3
3
  # See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details.
4
4
 
5
+ require 'new_relic/agent/http_clients/abstract_request'
6
+
5
7
  module NewRelic
6
8
  module Agent
7
9
  module HTTPClients
@@ -26,7 +28,7 @@ module NewRelic
26
28
  end
27
29
  end
28
30
 
29
- class HTTPClientRequest
31
+ class HTTPClientRequest < AbstractRequest
30
32
  attr_reader :request, :uri
31
33
 
32
34
  HTTP_CLIENT = "HTTPClient".freeze
@@ -2,10 +2,12 @@
2
2
  # This file is distributed under New Relic's license terms.
3
3
  # See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details.
4
4
 
5
+ require 'new_relic/agent/http_clients/abstract_request'
6
+
5
7
  module NewRelic
6
8
  module Agent
7
9
  module HTTPClients
8
- class NetHTTPRequest
10
+ class NetHTTPRequest < AbstractRequest
9
11
  def initialize(connection, request)
10
12
  @connection = connection
11
13
  @request = request
@@ -2,6 +2,8 @@
2
2
  # This file is distributed under New Relic's license terms.
3
3
  # See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details.
4
4
 
5
+ require 'new_relic/agent/http_clients/abstract_request'
6
+
5
7
  module NewRelic
6
8
  module Agent
7
9
  module HTTPClients
@@ -35,7 +37,7 @@ module NewRelic
35
37
  end
36
38
  end
37
39
 
38
- class TyphoeusHTTPRequest
40
+ class TyphoeusHTTPRequest < AbstractRequest
39
41
  def initialize(request)
40
42
  @request = request
41
43
  @uri = case request.url
@@ -51,8 +51,8 @@ module NewRelic
51
51
 
52
52
  state.sql_sampler_transaction_data = TransactionSqlData.new
53
53
 
54
- if state.transaction_sample_builder
55
- guid = state.transaction_sample_builder.sample.guid
54
+ if state.current_transaction
55
+ guid = state.current_transaction.guid
56
56
  end
57
57
 
58
58
  if Agent.config[:'slow_sql.enabled'] && state.sql_sampler_transaction_data
@@ -0,0 +1,59 @@
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
+ module NewRelic
6
+ module Agent
7
+ class ThroughputMonitor
8
+
9
+ def initialize target_samples = 10
10
+ @target = target_samples
11
+ @seen = 0
12
+ @seen_last = 0
13
+ @sampled_count = 0
14
+ @first_cycle = true
15
+ @lock = Mutex.new
16
+ end
17
+
18
+ # Called at the beginning of each transaction, increments seen and
19
+ # returns a boolean indicating if we should mark the transaction as
20
+ # sampled. This uses the adaptive sampling algorithm.
21
+ def sampled?
22
+ @lock.synchronize do
23
+ sampled = if @first_cycle
24
+ @sampled_count < 10
25
+ elsif @sampled_count < @target
26
+ rand(@seen_last) < @target
27
+ else
28
+ rand(@seen) < (@target ** (@target / @sampled_count) - @target ** 0.51)
29
+ end
30
+
31
+ @sampled_count += 1 if sampled
32
+ @seen += 1
33
+
34
+ sampled
35
+ end
36
+ end
37
+
38
+ def reset!
39
+ @lock.synchronize do
40
+ @first_cycle = false
41
+ @seen_last = @seen
42
+ @seen = 0
43
+ @sampled_count = 0
44
+ end
45
+ end
46
+
47
+ def stats
48
+ @lock.synchronize do
49
+ {
50
+ target: @target,
51
+ seen: @seen,
52
+ seen_last: @seen_last,
53
+ sampled_count: @sampled_count
54
+ }
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -9,6 +9,7 @@ require 'new_relic/agent/method_tracer_helpers'
9
9
  require 'new_relic/agent/transaction/attributes'
10
10
  require 'new_relic/agent/transaction/request_attributes'
11
11
  require 'new_relic/agent/transaction/tracing'
12
+ require 'new_relic/agent/transaction/distributed_tracing'
12
13
  require 'new_relic/agent/cross_app_tracing'
13
14
 
14
15
  module NewRelic
@@ -19,6 +20,7 @@ module NewRelic
19
20
  # @api public
20
21
  class Transaction
21
22
  include Tracing
23
+ include DistributedTracing
22
24
 
23
25
  # for nested transactions
24
26
  SUBTRANSACTION_PREFIX = 'Nested/'.freeze
@@ -55,7 +57,8 @@ module NewRelic
55
57
  :process_cpu_start,
56
58
  :http_response_code,
57
59
  :response_content_length,
58
- :response_content_type
60
+ :response_content_type,
61
+ :sampled
59
62
 
60
63
  attr_reader :guid,
61
64
  :metrics,
@@ -66,7 +69,8 @@ module NewRelic
66
69
  :attributes,
67
70
  :payload,
68
71
  :nesting_max_depth,
69
- :segments
72
+ :segments,
73
+ :end_time
70
74
 
71
75
  # Populated with the trace sample once this transaction is completed.
72
76
  attr_reader :transaction_trace
@@ -261,6 +265,7 @@ module NewRelic
261
265
 
262
266
  @category = category
263
267
  @start_time = Time.now
268
+ @end_time = nil
264
269
  @apdex_start = options[:apdex_start_time] || @start_time
265
270
  @jruby_cpu_start = jruby_cpu_time
266
271
  @process_cpu_start = process_cpu
@@ -277,6 +282,12 @@ module NewRelic
277
282
  @ignore_enduser = options.fetch(:ignore_enduser, false)
278
283
  @ignore_trace = false
279
284
 
285
+ if Agent.config[:'distributed_tracing.enabled']
286
+ @sampled = NewRelic::Agent.instance.throughput_monitor.sampled?
287
+ else
288
+ @sampled = nil
289
+ end
290
+
280
291
  @attributes = Attributes.new(NewRelic::Agent.instance.attribute_filter)
281
292
 
282
293
  merge_request_parameters(@filtered_params)
@@ -288,6 +299,10 @@ module NewRelic
288
299
  end
289
300
  end
290
301
 
302
+ def sampled?
303
+ @sampled
304
+ end
305
+
291
306
  def referer
292
307
  @request_attributes && @request_attributes.referer
293
308
  end
@@ -370,6 +385,16 @@ module NewRelic
370
385
  @default_name || NewRelic::Agent::UNKNOWN_METRIC
371
386
  end
372
387
 
388
+ # For common interface with Trace
389
+ alias_method :transaction_name, :best_name
390
+
391
+ attr_accessor :xray_session_id
392
+
393
+ def duration
394
+ (@end_time - @start_time).to_f
395
+ end
396
+ # End common interface
397
+
373
398
  def name_set?
374
399
  (@overridden_name || @default_name) ? true : false
375
400
  end
@@ -408,7 +433,6 @@ module NewRelic
408
433
  def start(state)
409
434
  return if !state.is_execution_traced?
410
435
 
411
- transaction_sampler.on_start_transaction(state, start_time)
412
436
  sql_sampler.on_start_transaction(state, start_time, request_path)
413
437
  NewRelic::Agent.instance.events.notify(:start_transaction)
414
438
  NewRelic::Agent::BusyCalculator.dispatcher_start(start_time)
@@ -492,6 +516,8 @@ module NewRelic
492
516
 
493
517
  def stop(state, end_time, outermost_frame)
494
518
  return if !state.is_execution_traced?
519
+
520
+ @end_time = end_time
495
521
  freeze_name_and_execute_if_not_ignored
496
522
 
497
523
  if nesting_max_depth == 1
@@ -515,20 +541,21 @@ module NewRelic
515
541
  end
516
542
 
517
543
  def commit!(state, end_time, outermost_node_name)
544
+ generate_payload(state, start_time, end_time)
545
+
518
546
  assign_agent_attributes
519
547
  assign_intrinsics(state)
520
548
 
549
+ segments.each { |s| s.finalize }
550
+
521
551
  @transaction_trace = transaction_sampler.on_finishing_transaction(state, self, end_time)
522
552
  sql_sampler.on_finishing_transaction(state, @frozen_name)
523
553
 
524
- segments.each { |s| s.record_metrics if s.record_metrics? }
525
554
  record_summary_metrics(outermost_node_name, end_time)
526
555
  record_apdex(state, end_time) unless ignore_apdex?
527
556
  record_queue_time
528
557
  record_client_application_metric state
529
558
 
530
- generate_payload(state, start_time, end_time)
531
-
532
559
  record_exceptions
533
560
  record_transaction_event
534
561
  merge_metrics
@@ -563,6 +590,8 @@ module NewRelic
563
590
  end
564
591
 
565
592
  def assign_intrinsics(state)
593
+ attributes.add_intrinsic_attribute :'nr.sampled', sampled?
594
+
566
595
  if gc_time = calculate_gc_time
567
596
  attributes.add_intrinsic_attribute(:gc_time, gc_time)
568
597
  end
@@ -577,9 +606,11 @@ module NewRelic
577
606
  attributes.add_intrinsic_attribute(:synthetics_monitor_id, synthetics_monitor_id)
578
607
  end
579
608
 
580
- if state.is_cross_app?
581
- attributes.add_intrinsic_attribute(:trip_id, cat_trip_id(state))
582
- attributes.add_intrinsic_attribute(:path_hash, cat_path_hash(state))
609
+ if distributed_trace_payload || order > 0
610
+ assign_distributed_tracing_intrinsics
611
+ elsif state.is_cross_app?
612
+ attributes.add_intrinsic_attribute(:trip_id, cat_trip_id)
613
+ attributes.add_intrinsic_attribute(:path_hash, cat_path_hash)
583
614
  end
584
615
  end
585
616
 
@@ -613,7 +644,11 @@ module NewRelic
613
644
  :attributes => @attributes,
614
645
  :error => false
615
646
  }
647
+
648
+ @payload[:'nr.sampled'] = sampled? if Agent.config[:'distributed_tracing.enabled']
649
+
616
650
  append_cat_info(state, duration, @payload)
651
+ append_distributed_tracing_info(@payload)
617
652
  append_apdex_perf_zone(duration, @payload)
618
653
  append_synthetics_to(state, @payload)
619
654
  append_referring_transaction_guid_to(state, @payload)
@@ -623,11 +658,11 @@ module NewRelic
623
658
  state.is_cross_app? || is_synthetics_request?
624
659
  end
625
660
 
626
- def cat_trip_id(state)
661
+ def cat_trip_id
627
662
  NewRelic::Agent.instance.cross_app_monitor.client_referring_transaction_trip_id(state) || guid
628
663
  end
629
664
 
630
- def cat_path_hash(state)
665
+ def cat_path_hash
631
666
  referring_path_hash = cat_referring_path_hash(state) || '0'
632
667
  seed = referring_path_hash.to_i(16)
633
668
  result = NewRelic::Agent.instance.cross_app_monitor.path_hash(best_name, seed)
@@ -702,8 +737,8 @@ module NewRelic
702
737
  payload[:guid] = guid
703
738
 
704
739
  return unless state.is_cross_app?
705
- trip_id = cat_trip_id(state)
706
- path_hash = cat_path_hash(state)
740
+ trip_id = cat_trip_id
741
+ path_hash = cat_path_hash
707
742
  referring_path_hash = cat_referring_path_hash(state)
708
743
 
709
744
  payload[:cat_trip_id] = trip_id if trip_id
@@ -847,6 +882,15 @@ module NewRelic
847
882
  Agent.config[key] && Agent.config[key][best_name]
848
883
  end
849
884
 
885
+ def threshold
886
+ source_class = Agent.config.source(:'transaction_tracer.transaction_threshold').class
887
+ if source_class == Configuration::DefaultSource
888
+ apdex_t * 4
889
+ else
890
+ Agent.config[:'transaction_tracer.transaction_threshold']
891
+ end
892
+ end
893
+
850
894
  def with_database_metric_name(model, method, product=nil)
851
895
  previous = self.instrumentation_state[:datastore_override]
852
896
  model_name = case model
@@ -925,8 +969,8 @@ module NewRelic
925
969
  state.is_cross_app_caller = true
926
970
  CrossAppTracing.insert_message_headers headers,
927
971
  guid,
928
- cat_trip_id(state),
929
- cat_path_hash(state),
972
+ cat_trip_id,
973
+ cat_path_hash,
930
974
  raw_synthetics_header
931
975
  end
932
976