newrelic_rpm 5.4.0.347 → 5.5.0.348

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +25 -0
  3. data/config.dot +0 -7
  4. data/lib/new_relic/agent/cross_app_monitor.rb +34 -64
  5. data/lib/new_relic/agent/cross_app_payload.rb +43 -0
  6. data/lib/new_relic/agent/cross_app_tracing.rb +93 -5
  7. data/lib/new_relic/agent/external.rb +17 -24
  8. data/lib/new_relic/agent/instrumentation/action_controller_subscriber.rb +3 -1
  9. data/lib/new_relic/agent/instrumentation/active_job.rb +12 -9
  10. data/lib/new_relic/agent/instrumentation/active_record_subscriber.rb +7 -15
  11. data/lib/new_relic/agent/instrumentation/controller_instrumentation.rb +8 -8
  12. data/lib/new_relic/agent/instrumentation/curb.rb +14 -6
  13. data/lib/new_relic/agent/instrumentation/data_mapper.rb +1 -1
  14. data/lib/new_relic/agent/instrumentation/excon/middleware.rb +1 -1
  15. data/lib/new_relic/agent/instrumentation/http.rb +1 -1
  16. data/lib/new_relic/agent/instrumentation/httpclient.rb +1 -1
  17. data/lib/new_relic/agent/instrumentation/memcache.rb +1 -1
  18. data/lib/new_relic/agent/instrumentation/memcache/dalli.rb +3 -3
  19. data/lib/new_relic/agent/instrumentation/mongo.rb +1 -1
  20. data/lib/new_relic/agent/instrumentation/mongodb_command_subscriber.rb +3 -4
  21. data/lib/new_relic/agent/instrumentation/redis.rb +3 -3
  22. data/lib/new_relic/agent/javascript_instrumentor.rb +14 -8
  23. data/lib/new_relic/agent/messaging.rb +14 -14
  24. data/lib/new_relic/agent/system_info.rb +3 -7
  25. data/lib/new_relic/agent/threading/agent_thread.rb +4 -2
  26. data/lib/new_relic/agent/transaction.rb +46 -120
  27. data/lib/new_relic/agent/transaction/abstract_segment.rb +4 -0
  28. data/lib/new_relic/agent/transaction/datastore_segment.rb +3 -2
  29. data/lib/new_relic/agent/transaction/external_request_segment.rb +3 -3
  30. data/lib/new_relic/agent/transaction/message_broker_segment.rb +1 -2
  31. data/lib/new_relic/agent/transaction/tracing.rb +1 -0
  32. data/lib/new_relic/agent/transaction_sampler.rb +1 -8
  33. data/lib/new_relic/agent/transaction_state.rb +122 -76
  34. data/lib/new_relic/version.rb +1 -1
  35. data/lib/sequel/extensions/newrelic_instrumentation.rb +2 -2
  36. data/lib/sequel/plugins/newrelic_instrumentation.rb +1 -1
  37. metadata +3 -3
  38. data/lib/new_relic/agent/transaction_timings.rb +0 -57
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 979f362569155df00b770dba3907bd0eb894169f70b712085ae19d16852f53a7
4
- data.tar.gz: b67e7d1c0cb1b5831f06d29c574f8efea5a95405a809b97c55add2e40d0cb101
3
+ metadata.gz: b17c73d708623383395fecf3a60a063566a12b59a10d2b18d686deef72f31773
4
+ data.tar.gz: f9b9275cf1f9f980175454d8ed5f06a4fa2d173c4edf52e136580c96b2e05eda
5
5
  SHA512:
6
- metadata.gz: 2974f98ab0c47eb9f1c0e2014fb4122138fe4d1bf4d5265f8d09d1583f7ee8fc3f9ab5cb86bf1dc7e31d8b44d5762e56c8e1edd421120151ae85e3e2e4bd854d
7
- data.tar.gz: 7d16296c46075e1e84824afaeff67db60ea39e0b5b4032bbae9bbcbde8c6c5ca6dc6d40618c92cbf29391f0ba36876937e8d2444313d3805031827a7e8313607
6
+ metadata.gz: 8cc16f8281e2e00829add33da0ba8053afbfe29e56b9e901c40d40cd44be982df5859492a26041aed7593b687316848113dcc02b646816e2812132bda4a86db5
7
+ data.tar.gz: 7eecd067c7606152a08e6485c6f0a642f5e73081812b011f89ba8d2b3fdd74df66ee0686e385d078ec4a4286e396a1ee9206bbc9c2585925596b9b7e90101015
@@ -1,5 +1,30 @@
1
1
  # New Relic Ruby Agent Release Notes #
2
2
 
3
+ ## v5.5.0
4
+
5
+ * Bugfix for `perform` instrumentation with curb gem
6
+
7
+ Use of curb's `perform` method now no longer results in nil headers
8
+ getting returned.
9
+
10
+ * Bugfix for parsing Docker container IDs
11
+
12
+ The agent now parses Docker container IDs correctly regardless of the
13
+ cgroup parent.
14
+
15
+ * Use lazy load hooks for ActiveJob instrumentation
16
+
17
+ In some instances the ActiveJob instrumentation could trigger ActiveJob
18
+ to load before it was initialized by Rails. This could result in
19
+ configuration changes not being properly applied. The agent now uses lazy
20
+ load hooks which fixes this issue.
21
+
22
+ * Documentation improvement
23
+
24
+ The `config.dot` diagram of the agent's configuration settings no
25
+ longer includes the deleted `developer_mode` option. Thanks to
26
+ Yuichiro Kaneko for the contribution!
27
+
3
28
  ## v5.4.0
4
29
 
5
30
  * Capacity analysis for multi-threaded dispatchers
data/config.dot CHANGED
@@ -1,7 +1,6 @@
1
1
  digraph AgentEnabled {
2
2
  node[color=green]
3
3
  "[agent_enabled]"
4
- "[developer_mode]"
5
4
  "[monitor_mode]"
6
5
  "[slow_sql.explain_threshold]"
7
6
  "[slow_sql.stack_trace_threshold]"
@@ -198,7 +197,6 @@ digraph AgentEnabled {
198
197
  "Agent#validate_settings" -> "[validate_token]"
199
198
  "Agent#log_error" -> "Control#server"
200
199
  "Agent#monitoring?" -> "[monitor_mode]"
201
- "Agent#check_trasaction_sampler_status" -> "[developer_mode]"
202
200
  "Agent#check_sql_sampler_status" -> "[slow_sql.enabled]"
203
201
  "Agent#check_sql_sampler_status" -> "[slow_sql.record_sql]"
204
202
  "Agent#check_sql_sampler_status" -> "[transaction_tracer.enabled]"
@@ -213,7 +211,6 @@ digraph AgentEnabled {
213
211
  "MethodTracer#remove_method_tracer" -> "[agent_enabled]"
214
212
 
215
213
  "Rails#init_config" -> "[agent_enabled]"
216
- "Rails#init_config" -> "[developer_mode]"
217
214
 
218
215
  "ErrorCollector#initialize" -> "[error_collector.enabled]"
219
216
  "ErrorCollector#initialize" -> "[error_collector.capture_source]"
@@ -237,10 +234,6 @@ digraph AgentEnabled {
237
234
  "TransactionSampler#configure!" -> "[transaction_tracer.explain_threshold]"
238
235
  "TransactionSampler#configure!" -> "[transaction_tracer.explain_enabled]"
239
236
  "TransactionSampler#configure!" -> "[transaction_tracer.transaction_threshold]"
240
- "TransactionSampler#configure!" -> "[developer_mode]"
241
- "TransactionSampler#notice_push_scope" -> "[developer_mode]"
242
- "TransactionSampler#capture_segment_trace" -> "[developer_mode]"
243
- "TransactionSampler#store_segment_for_developer_mode" -> "[developer_mode]"
244
237
 
245
238
  "NoticedError#initialize" -> "[high_security]"
246
239
 
@@ -8,14 +8,15 @@ require 'json'
8
8
  require 'new_relic/agent/inbound_request_monitor'
9
9
  require 'new_relic/agent/transaction_state'
10
10
  require 'new_relic/agent/threading/agent_thread'
11
+ require 'new_relic/agent/cross_app_payload'
11
12
 
12
13
  module NewRelic
13
14
  module Agent
14
15
  class CrossAppMonitor < InboundRequestMonitor
15
16
 
16
- NEWRELIC_ID_HEADER = 'X-NewRelic-ID'
17
- NEWRELIC_TXN_HEADER = 'X-NewRelic-Transaction'
18
- NEWRELIC_APPDATA_HEADER = 'X-NewRelic-App-Data'
17
+ NEWRELIC_ID_HEADER = 'X-NewRelic-ID'.freeze
18
+ NEWRELIC_TXN_HEADER = 'X-NewRelic-Transaction'.freeze
19
+ NEWRELIC_APPDATA_HEADER = 'X-NewRelic-App-Data'.freeze
19
20
 
20
21
  NEWRELIC_ID_HEADER_KEY = 'HTTP_X_NEWRELIC_ID'.freeze
21
22
  NEWRELIC_TXN_HEADER_KEY = 'HTTP_X_NEWRELIC_TRANSACTION'.freeze
@@ -25,6 +26,15 @@ module NewRelic
25
26
  register_event_listeners(events)
26
27
  end
27
28
 
29
+ def path_hash(txn_name, seed)
30
+ rotated = ((seed << 1) | (seed >> 31)) & 0xffffffff
31
+ app_name = NewRelic::Agent.config.app_names.first
32
+ identifier = "#{app_name};#{txn_name}"
33
+ sprintf("%08x", rotated ^ hash_transaction_name(identifier))
34
+ end
35
+
36
+ private
37
+
28
38
  # Expected sequence of events:
29
39
  # :before_call will save our cross application request id to the thread
30
40
  # :after_call will write our response headers/metrics and clean up the thread
@@ -36,8 +46,13 @@ module NewRelic
36
46
  if id = decoded_id(env) and should_process_request?(id)
37
47
  state = NewRelic::Agent::TransactionState.tl_get
38
48
 
39
- state.client_cross_app_id = id
40
- save_referring_transaction_info(state, env)
49
+ if (transaction = state.current_transaction)
50
+ transaction_info = referring_transaction_info(state, env)
51
+
52
+ payload = CrossAppPayload.new(id, transaction, transaction_info)
53
+ transaction.cross_app_payload = payload
54
+ end
55
+
41
56
  CrossAppTracing.assign_intrinsic_transaction_attributes state
42
57
  end
43
58
  end
@@ -47,76 +62,38 @@ module NewRelic
47
62
 
48
63
  insert_response_header(state, env, headers)
49
64
  end
50
-
51
65
  end
52
66
 
53
- def save_referring_transaction_info(state, request_headers)
67
+ def referring_transaction_info(state, request_headers)
54
68
  txn_header = request_headers[NEWRELIC_TXN_HEADER_KEY] or return
55
- txn_info = deserialize_header(txn_header, NEWRELIC_TXN_HEADER)
56
- state.referring_transaction_info = txn_info
57
- end
58
-
59
- def client_referring_transaction_guid(state)
60
- info = state.referring_transaction_info or return nil
61
- return info[0]
62
- end
63
-
64
- def client_referring_transaction_record_flag(state)
65
- info = state.referring_transaction_info or return nil
66
- return info[1]
67
- end
68
-
69
- def client_referring_transaction_trip_id(state)
70
- info = state.referring_transaction_info or return nil
71
- return info[2].is_a?(String) && info[2]
72
- end
73
-
74
- def client_referring_transaction_path_hash(state)
75
- info = state.referring_transaction_info or return nil
76
- return info[3].is_a?(String) && info[3]
69
+ deserialize_header(txn_header, NEWRELIC_TXN_HEADER)
77
70
  end
78
71
 
79
72
  def insert_response_header(state, request_headers, response_headers)
80
- unless state.client_cross_app_id.nil?
81
- txn = state.current_transaction
82
- unless txn.nil?
83
- txn.freeze_name_and_execute_if_not_ignored do
84
- timings = state.timings
85
- content_length = content_length_from_request(request_headers)
86
-
87
- set_response_headers(state, response_headers, timings, content_length)
88
- end
73
+ txn = state.current_transaction
74
+ unless txn.nil? || txn.cross_app_payload.nil?
75
+ txn.freeze_name_and_execute_if_not_ignored do
76
+ content_length = content_length_from_request(request_headers)
77
+ set_response_headers(txn, response_headers, content_length)
89
78
  end
90
79
  end
91
80
  end
92
81
 
93
82
  def should_process_request? id
94
- return cross_app_enabled? && CrossAppTracing.trusts?(id)
95
- end
96
-
97
- def cross_app_enabled?
98
- NewRelic::Agent::CrossAppTracing.cross_app_enabled?
83
+ CrossAppTracing.cross_app_enabled? && CrossAppTracing.trusts?(id)
99
84
  end
100
85
 
101
- def set_response_headers(state, response_headers, timings, content_length)
102
- response_headers[NEWRELIC_APPDATA_HEADER] = build_payload(state, timings, content_length)
103
- end
86
+ def set_response_headers(transaction, response_headers, content_length)
87
+ payload = obfuscator.obfuscate(
88
+ ::JSON.dump(
89
+ transaction.cross_app_payload.as_json_array(content_length)))
104
90
 
105
- def build_payload(state, timings, content_length)
106
- payload = [
107
- NewRelic::Agent.config[:cross_process_id],
108
- timings.transaction_name,
109
- timings.queue_time_in_seconds.to_f,
110
- timings.app_time_in_seconds.to_f,
111
- content_length,
112
- state.request_guid
113
- ]
114
- payload = obfuscator.obfuscate(::JSON.dump(payload))
91
+ response_headers[NEWRELIC_APPDATA_HEADER] = payload
115
92
  end
116
93
 
117
94
  def decoded_id(request)
118
95
  encoded_id = request[NEWRELIC_ID_HEADER_KEY]
119
- return "" if encoded_id.nil?
96
+ return "" if encoded_id.nil? || encoded_id.empty?
120
97
 
121
98
  obfuscator.deobfuscate(encoded_id)
122
99
  end
@@ -128,13 +105,6 @@ module NewRelic
128
105
  def hash_transaction_name(identifier)
129
106
  Digest::MD5.digest(identifier).unpack("@12N").first & 0xffffffff
130
107
  end
131
-
132
- def path_hash(txn_name, seed)
133
- rotated = ((seed << 1) | (seed >> 31)) & 0xffffffff
134
- app_name = NewRelic::Agent.config.app_names.first
135
- identifier = "#{app_name};#{txn_name}"
136
- sprintf("%08x", rotated ^ hash_transaction_name(identifier))
137
- end
138
108
  end
139
109
  end
140
110
  end
@@ -0,0 +1,43 @@
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 CrossAppPayload
8
+ attr_reader :id, :transaction, :referring_guid, :referring_trip_id, :referring_path_hash
9
+
10
+ def initialize(id, transaction, transaction_info)
11
+ @id = id
12
+ @transaction = transaction
13
+
14
+ transaction_info ||= []
15
+ @referring_guid = transaction_info[0]
16
+ # unused_flag = transaction_info[1]
17
+ @referring_trip_id = string_or_false_for(transaction_info[2])
18
+ @referring_path_hash = string_or_false_for(transaction_info[3])
19
+ end
20
+
21
+ def as_json_array(content_length)
22
+ queue_time_in_seconds = [transaction.queue_time.to_f, 0.0].max
23
+ start_time_in_seconds = [transaction.start_time.to_f, 0.0].max
24
+ app_time_in_seconds = Time.now.to_f - start_time_in_seconds
25
+
26
+ [
27
+ NewRelic::Agent.config[:cross_process_id],
28
+ transaction.best_name,
29
+ queue_time_in_seconds.to_f,
30
+ app_time_in_seconds.to_f,
31
+ content_length,
32
+ transaction.guid
33
+ ]
34
+ end
35
+
36
+ private
37
+
38
+ def string_or_false_for(value)
39
+ value.is_a?(String) && value
40
+ end
41
+ end
42
+ end
43
+ end
@@ -8,7 +8,6 @@ require 'json'
8
8
  module NewRelic
9
9
  module Agent
10
10
  module CrossAppTracing
11
-
12
11
  # The cross app response header for "outgoing" calls
13
12
  NR_APPDATA_HEADER = 'X-NewRelic-App-Data'.freeze
14
13
 
@@ -22,6 +21,93 @@ module NewRelic
22
21
  NR_MESSAGE_BROKER_TXN_HEADER = 'NewRelicTransaction'.freeze
23
22
  NR_MESSAGE_BROKER_SYNTHETICS_HEADER = 'NewRelicSynthetics'.freeze
24
23
 
24
+ attr_accessor :is_cross_app_caller, :cross_app_payload, :cat_path_hashes
25
+
26
+ def is_cross_app_caller?
27
+ @is_cross_app_caller = false unless defined? @is_cross_app_caller
28
+ @is_cross_app_caller
29
+ end
30
+
31
+ def is_cross_app_callee?
32
+ cross_app_payload != nil
33
+ end
34
+
35
+ def is_cross_app?
36
+ is_cross_app_caller? || is_cross_app_callee?
37
+ end
38
+
39
+ def cat_trip_id
40
+ cross_app_payload && cross_app_payload.referring_trip_id || guid
41
+ end
42
+
43
+ def cat_path_hash
44
+ referring_path_hash = cat_referring_path_hash || '0'
45
+ seed = referring_path_hash.to_i(16)
46
+ result = NewRelic::Agent.instance.cross_app_monitor.path_hash(best_name, seed)
47
+ record_cat_path_hash(result)
48
+ result
49
+ end
50
+
51
+ def add_message_cat_headers headers
52
+ self.is_cross_app_caller = true
53
+ CrossAppTracing.insert_message_headers headers,
54
+ guid,
55
+ cat_trip_id,
56
+ cat_path_hash,
57
+ raw_synthetics_header
58
+ end
59
+
60
+ private
61
+
62
+ def record_cat_path_hash(hash)
63
+ @cat_path_hashes ||= []
64
+ if @cat_path_hashes.size < 10 && !@cat_path_hashes.include?(hash)
65
+ @cat_path_hashes << hash
66
+ end
67
+ end
68
+
69
+ def cat_referring_path_hash
70
+ cross_app_payload && cross_app_payload.referring_path_hash
71
+ end
72
+
73
+ def append_cat_info(payload)
74
+ if (referring_guid = cross_app_payload && cross_app_payload.referring_guid)
75
+ payload[:referring_transaction_guid] = referring_guid
76
+ end
77
+
78
+ return unless include_guid?
79
+ payload[:guid] = guid
80
+
81
+ return unless is_cross_app?
82
+ trip_id = cat_trip_id
83
+ path_hash = cat_path_hash
84
+ referring_path_hash = cat_referring_path_hash
85
+
86
+ payload[:cat_trip_id] = trip_id if trip_id
87
+ payload[:cat_referring_path_hash] = referring_path_hash if referring_path_hash
88
+
89
+ if path_hash
90
+ payload[:cat_path_hash] = path_hash
91
+
92
+ alternate_path_hashes = cat_path_hashes - [path_hash]
93
+ unless alternate_path_hashes.empty?
94
+ payload[:cat_alternate_path_hashes] = alternate_path_hashes
95
+ end
96
+ end
97
+ end
98
+
99
+ def assign_cross_app_intrinsics
100
+ attributes.add_intrinsic_attribute(:trip_id, cat_trip_id)
101
+ attributes.add_intrinsic_attribute(:path_hash, cat_path_hash)
102
+ end
103
+
104
+ def record_cross_app_metrics
105
+ if (id = cross_app_payload && cross_app_payload.id)
106
+ app_time_in_seconds = [Time.now.to_f - @start_time.to_f, 0.0].max
107
+ NewRelic::Agent.record_metric "ClientApplication/#{id}/all", app_time_in_seconds
108
+ end
109
+ end
110
+
25
111
  ###############
26
112
  module_function
27
113
  ###############
@@ -127,16 +213,18 @@ module NewRelic
127
213
  valid_cross_app_id?(id) && trusts?(id)
128
214
  end
129
215
 
216
+ # From inbound request headers
130
217
  def assign_intrinsic_transaction_attributes state
131
218
  # We expect to get the before call to set the id (if we have it) before
132
219
  # this, and then write our custom parameter when the transaction starts
133
- return unless transaction = state.current_transaction
220
+ return unless (transaction = state.current_transaction)
221
+ return unless (cross_app_payload = transaction.cross_app_payload)
134
222
 
135
- if state.client_cross_app_id
136
- transaction.attributes.add_intrinsic_attribute(:client_cross_process_id, state.client_cross_app_id)
223
+ if (cross_app_id = cross_app_payload.id)
224
+ transaction.attributes.add_intrinsic_attribute(:client_cross_process_id, cross_app_id)
137
225
  end
138
226
 
139
- if referring_guid = state.referring_transaction_info && state.referring_transaction_info[0]
227
+ if (referring_guid = cross_app_payload.referring_guid)
140
228
  transaction.attributes.add_intrinsic_attribute(:referring_transaction_guid, referring_guid)
141
229
  end
142
230
  end
@@ -4,6 +4,7 @@
4
4
 
5
5
  require 'new_relic/agent/transaction/tracing'
6
6
  require 'new_relic/agent/cross_app_tracing'
7
+ require 'new_relic/agent/cross_app_payload'
7
8
 
8
9
  module NewRelic
9
10
  module Agent
@@ -49,6 +50,7 @@ module NewRelic
49
50
  NON_HTTP_CAT_ID_HEADER = 'NewRelicID'.freeze
50
51
  NON_HTTP_CAT_TXN_HEADER = 'NewRelicTransaction'.freeze
51
52
  NON_HTTP_CAT_SYNTHETICS_HEADER = 'NewRelicSynthetics'.freeze
53
+ NON_HTTP_CAT_CONTENT_LENGTH = -1
52
54
 
53
55
  # Process obfuscated +String+ indentifying a calling application and transaction that is also running a
54
56
  # New Relic agent and save information in current transaction for inclusion in a trace. The +String+ is
@@ -69,12 +71,12 @@ module NewRelic
69
71
  # handle/check ID
70
72
  #
71
73
  if id = rmd[NON_HTTP_CAT_ID_HEADER] and CrossAppTracing.trusted_valid_cross_app_id?(id)
72
- state.client_cross_app_id = id
73
-
74
74
  # handle transaction info
75
75
  #
76
76
  if txn_info = rmd[NON_HTTP_CAT_TXN_HEADER]
77
- state.referring_transaction_info = txn_info
77
+ payload = CrossAppPayload.new(id, transaction, txn_info)
78
+ transaction.cross_app_payload = payload
79
+
78
80
  CrossAppTracing.assign_intrinsic_transaction_attributes state
79
81
  end
80
82
 
@@ -108,30 +110,21 @@ module NewRelic
108
110
  return unless CrossAppTracing.cross_app_enabled?
109
111
 
110
112
  state = NewRelic::Agent::TransactionState.tl_get
111
- if transaction = state.current_transaction and state.client_cross_app_id
113
+ return unless (transaction = state.current_transaction)
114
+ return unless (cross_app_payload = transaction.cross_app_payload)
112
115
 
113
- # must freeze the name since we're responding with it
116
+ # must freeze the name since we're responding with it
117
+ #
118
+ transaction.freeze_name_and_execute_if_not_ignored do
119
+ # build response payload
114
120
  #
115
- transaction.freeze_name_and_execute_if_not_ignored do
121
+ rmd = {
122
+ NewRelicAppData: cross_app_payload.as_json_array(NON_HTTP_CAT_CONTENT_LENGTH)
123
+ }
116
124
 
117
- # build response payload
118
- #
119
- rmd = {
120
- NewRelicAppData: [
121
- NewRelic::Agent.config[:cross_process_id],
122
- state.timings.transaction_name,
123
- state.timings.queue_time_in_seconds.to_f,
124
- state.timings.app_time_in_seconds.to_f,
125
- -1, # per non-HTTP CAT spec
126
- state.request_guid
127
- ]
128
- }
129
-
130
- # obfuscate the generated response metadata JSON
131
- #
132
- obfuscator.obfuscate ::JSON.dump(rmd)
133
-
134
- end
125
+ # obfuscate the generated response metadata JSON
126
+ #
127
+ obfuscator.obfuscate ::JSON.dump(rmd)
135
128
  end
136
129
  rescue => e
137
130
  NewRelic::Agent.logger.error "error during get_response_metadata", e