newrelic_rpm 5.5.0.348 → 6.2.0.354

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/.gitignore +1 -0
  3. data/.travis.yml +36 -84
  4. data/.yardopts +1 -0
  5. data/CHANGELOG.md +149 -0
  6. data/README.md +1 -1
  7. data/config.dot +1 -0
  8. data/lib/new_relic/agent/agent.rb +15 -11
  9. data/lib/new_relic/agent/attribute_filter.rb +77 -17
  10. data/lib/new_relic/agent/configuration/default_source.rb +71 -1
  11. data/lib/new_relic/agent/configuration/security_policy_source.rb +14 -0
  12. data/lib/new_relic/agent/configuration/server_source.rb +2 -1
  13. data/lib/new_relic/agent/cross_app_monitor.rb +3 -3
  14. data/lib/new_relic/agent/datastores/metric_helper.rb +1 -2
  15. data/lib/new_relic/agent/datastores.rb +6 -8
  16. data/lib/new_relic/agent/distributed_trace_monitor.rb +1 -2
  17. data/lib/new_relic/agent/distributed_trace_payload.rb +7 -11
  18. data/lib/new_relic/agent/error_collector.rb +4 -6
  19. data/lib/new_relic/agent/external.rb +6 -4
  20. data/lib/new_relic/agent/hostname.rb +8 -0
  21. data/lib/new_relic/agent/instrumentation/action_cable_subscriber.rb +8 -5
  22. data/lib/new_relic/agent/instrumentation/action_controller_subscriber.rb +12 -8
  23. data/lib/new_relic/agent/instrumentation/action_view_subscriber.rb +1 -1
  24. data/lib/new_relic/agent/instrumentation/active_job.rb +6 -7
  25. data/lib/new_relic/agent/instrumentation/active_record.rb +2 -2
  26. data/lib/new_relic/agent/instrumentation/active_record_subscriber.rb +3 -3
  27. data/lib/new_relic/agent/instrumentation/active_storage.rb +23 -0
  28. data/lib/new_relic/agent/instrumentation/active_storage_subscriber.rb +59 -0
  29. data/lib/new_relic/agent/instrumentation/acts_as_solr.rb +1 -1
  30. data/lib/new_relic/agent/instrumentation/bunny.rb +16 -12
  31. data/lib/new_relic/agent/instrumentation/controller_instrumentation.rb +9 -3
  32. data/lib/new_relic/agent/instrumentation/curb.rb +18 -5
  33. data/lib/new_relic/agent/instrumentation/data_mapper.rb +1 -1
  34. data/lib/new_relic/agent/instrumentation/evented_subscriber.rb +1 -1
  35. data/lib/new_relic/agent/instrumentation/excon/connection.rb +1 -1
  36. data/lib/new_relic/agent/instrumentation/grape.rb +17 -4
  37. data/lib/new_relic/agent/instrumentation/middleware_proxy.rb +1 -1
  38. data/lib/new_relic/agent/instrumentation/middleware_tracing.rb +11 -4
  39. data/lib/new_relic/agent/instrumentation/net.rb +1 -1
  40. data/lib/new_relic/agent/instrumentation/rake.rb +2 -3
  41. data/lib/new_relic/agent/instrumentation/sequel.rb +1 -1
  42. data/lib/new_relic/agent/instrumentation/typhoeus.rb +3 -3
  43. data/lib/new_relic/agent/javascript_instrumentor.rb +1 -1
  44. data/lib/new_relic/agent/messaging.rb +9 -8
  45. data/lib/new_relic/agent/method_tracer_helpers.rb +2 -2
  46. data/lib/new_relic/agent/new_relic_service/json_marshaller.rb +0 -1
  47. data/lib/new_relic/agent/new_relic_service/marshaller.rb +5 -26
  48. data/lib/new_relic/agent/new_relic_service.rb +69 -25
  49. data/lib/new_relic/agent/span_event_primitive.rb +26 -16
  50. data/lib/new_relic/agent/sql_sampler.rb +3 -3
  51. data/lib/new_relic/agent/stats_engine.rb +2 -2
  52. data/lib/new_relic/agent/synthetics_monitor.rb +1 -2
  53. data/lib/new_relic/agent/system_info.rb +5 -0
  54. data/lib/new_relic/agent/threading/agent_thread.rb +1 -1
  55. data/lib/new_relic/agent/tracer.rb +462 -0
  56. data/lib/new_relic/agent/transaction/abstract_segment.rb +1 -1
  57. data/lib/new_relic/agent/transaction/datastore_segment.rb +0 -2
  58. data/lib/new_relic/agent/transaction/distributed_tracing.rb +1 -2
  59. data/lib/new_relic/agent/transaction/trace_node.rb +4 -2
  60. data/lib/new_relic/agent/transaction/tracing.rb +0 -99
  61. data/lib/new_relic/agent/transaction.rb +43 -88
  62. data/lib/new_relic/agent/transaction_time_aggregator.rb +49 -25
  63. data/lib/new_relic/agent/utilization_data.rb +36 -1
  64. data/lib/new_relic/agent.rb +8 -4
  65. data/lib/new_relic/build.rb +2 -2
  66. data/lib/new_relic/control/frameworks/rails6.rb +14 -0
  67. data/lib/new_relic/latest_changes.rb +2 -2
  68. data/lib/new_relic/rack/agent_middleware.rb +1 -1
  69. data/lib/new_relic/version.rb +2 -2
  70. data/newrelic_rpm.gemspec +2 -9
  71. data/test/agent_helper.rb +2 -2
  72. metadata +13 -39
  73. data/lib/new_relic/agent/transaction_state.rb +0 -186
@@ -57,40 +57,46 @@ module NewRelic
57
57
  def for_external_request_segment(segment)
58
58
  intrinsics = intrinsics_for(segment)
59
59
 
60
- intrinsics[HTTP_URL_KEY] = truncate(segment.uri)
61
60
  intrinsics[COMPONENT_KEY] = segment.library
62
61
  intrinsics[HTTP_METHOD_KEY] = segment.procedure
63
62
  intrinsics[CATEGORY_KEY] = HTTP_CATEGORY
64
63
  intrinsics[SPAN_KIND_KEY] = CLIENT
65
64
 
66
- [intrinsics, EMPTY_HASH, EMPTY_HASH]
65
+ agent_attributes = {}
66
+
67
+ if allowed?(HTTP_URL_KEY)
68
+ agent_attributes[HTTP_URL_KEY] = truncate(segment.uri)
69
+ end
70
+
71
+ [intrinsics, EMPTY_HASH, agent_attributes]
67
72
  end
68
73
 
69
74
  def for_datastore_segment(segment)
70
75
  intrinsics = intrinsics_for(segment)
71
76
 
72
77
  intrinsics[COMPONENT_KEY] = segment.product
78
+ intrinsics[SPAN_KIND_KEY] = CLIENT
79
+ intrinsics[CATEGORY_KEY] = DATASTORE_CATEGORY
80
+
81
+ agent_attributes = {}
73
82
 
74
- if segment.database_name
75
- intrinsics[DB_INSTANCE_KEY] = truncate(segment.database_name)
83
+ if segment.database_name && allowed?(DB_INSTANCE_KEY)
84
+ agent_attributes[DB_INSTANCE_KEY] = truncate(segment.database_name)
76
85
  end
77
- if segment.host && segment.port_path_or_id
78
- intrinsics[PEER_ADDRESS_KEY] = truncate("#{segment.host}:#{segment.port_path_or_id}")
86
+ if segment.host && segment.port_path_or_id && allowed?(PEER_ADDRESS_KEY)
87
+ agent_attributes[PEER_ADDRESS_KEY] = truncate("#{segment.host}:#{segment.port_path_or_id}")
79
88
  end
80
- if segment.host
81
- intrinsics[PEER_HOSTNAME_KEY] = truncate(segment.host)
89
+ if segment.host && allowed?(PEER_HOSTNAME_KEY)
90
+ agent_attributes[PEER_HOSTNAME_KEY] = truncate(segment.host)
82
91
  end
83
92
 
84
- intrinsics[SPAN_KIND_KEY] = CLIENT
85
- intrinsics[CATEGORY_KEY] = DATASTORE_CATEGORY
86
-
87
- if segment.sql_statement
88
- intrinsics[DB_STATEMENT_KEY] = truncate(segment.sql_statement.safe_sql, 2000)
89
- elsif segment.nosql_statement
90
- intrinsics[DB_STATEMENT_KEY] = truncate(segment.nosql_statement, 2000)
93
+ if segment.sql_statement && allowed?(DB_STATEMENT_KEY)
94
+ agent_attributes[DB_STATEMENT_KEY] = truncate(segment.sql_statement.safe_sql, 2000)
95
+ elsif segment.nosql_statement && allowed?(DB_STATEMENT_KEY)
96
+ agent_attributes[DB_STATEMENT_KEY] = truncate(segment.nosql_statement, 2000)
91
97
  end
92
98
 
93
- [intrinsics, EMPTY_HASH, EMPTY_HASH]
99
+ [intrinsics, EMPTY_HASH, agent_attributes]
94
100
  end
95
101
 
96
102
  private
@@ -139,6 +145,10 @@ module NewRelic
139
145
  value
140
146
  end
141
147
  end
148
+
149
+ def allowed?(key)
150
+ NewRelic::Agent.instance.attribute_filter.allows_key?(key, AttributeFilter::DST_SPAN_EVENTS)
151
+ end
142
152
  end
143
153
  end
144
154
  end
@@ -61,7 +61,7 @@ module NewRelic
61
61
  end
62
62
 
63
63
  def tl_transaction_data # only used for testing
64
- TransactionState.tl_get.sql_sampler_transaction_data
64
+ Tracer.state.sql_sampler_transaction_data
65
65
  end
66
66
 
67
67
  # This is called when we are done with the transaction.
@@ -141,7 +141,7 @@ module NewRelic
141
141
  # @deprecated Use {Datastores.notice_sql} instead.
142
142
  #
143
143
  def notice_sql(sql, metric_name, config, duration, state=nil, explainer=nil, binds=nil, name=nil) #THREAD_LOCAL_ACCESS sometimes
144
- state ||= TransactionState.tl_get
144
+ state ||= Tracer.state
145
145
  data = state.sql_sampler_transaction_data
146
146
  return unless data
147
147
 
@@ -169,7 +169,7 @@ module NewRelic
169
169
  end
170
170
 
171
171
  def notice_sql_statement(statement, metric_name, duration)
172
- state ||= TransactionState.tl_get
172
+ state ||= Tracer.state
173
173
  data = state.sql_sampler_transaction_data
174
174
  return unless data
175
175
 
@@ -61,7 +61,7 @@ module NewRelic
61
61
  # @api private
62
62
  #
63
63
  def tl_record_unscoped_metrics(metric_names, value=nil, aux=nil, &blk)
64
- state = NewRelic::Agent::TransactionState.tl_get
64
+ state = NewRelic::Agent::Tracer.state
65
65
  record_unscoped_metrics(state, metric_names, value, aux, &blk)
66
66
  end
67
67
 
@@ -97,7 +97,7 @@ module NewRelic
97
97
  # @api private
98
98
  #
99
99
  def tl_record_scoped_and_unscoped_metrics(scoped_metric, summary_metrics=nil, value=nil, aux=nil, &blk)
100
- state = NewRelic::Agent::TransactionState.tl_get
100
+ state = NewRelic::Agent::Tracer.state
101
101
  record_scoped_and_unscoped_metrics(state, scoped_metric, summary_metrics, value, aux, &blk)
102
102
  end
103
103
 
@@ -27,8 +27,7 @@ module NewRelic
27
27
  SyntheticsMonitor.is_supported_version?(incoming_payload) &&
28
28
  SyntheticsMonitor.is_trusted?(incoming_payload)
29
29
 
30
- state = NewRelic::Agent::TransactionState.tl_get
31
- txn = state.current_transaction
30
+ txn = Tracer.current_transaction
32
31
  txn.raw_synthetics_header = encoded_header
33
32
  txn.synthetics_payload = incoming_payload
34
33
  end
@@ -8,6 +8,7 @@
8
8
  # the requested information is unavailable.
9
9
 
10
10
  require 'rbconfig'
11
+ require 'socket'
11
12
 
12
13
  module NewRelic
13
14
  module Agent
@@ -30,6 +31,10 @@ module NewRelic
30
31
 
31
32
  @processor_info = nil
32
33
 
34
+ def self.ip_addresses
35
+ Socket.ip_address_list.map(&:ip_address)
36
+ end
37
+
33
38
  def self.clear_processor_info
34
39
  @processor_info = nil
35
40
  end
@@ -37,7 +37,7 @@ module NewRelic
37
37
  if thread.key?(:newrelic_label)
38
38
  profile_agent_code ? :agent : :ignore
39
39
  else
40
- state = TransactionState.tl_state_for(thread)
40
+ state = Tracer.state_for(thread)
41
41
  txn = state.current_transaction
42
42
 
43
43
  if txn && !txn.recording_web_transaction?
@@ -0,0 +1,462 @@
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'
6
+ require 'new_relic/agent/transaction/segment'
7
+ require 'new_relic/agent/transaction/datastore_segment'
8
+ require 'new_relic/agent/transaction/external_request_segment'
9
+ require 'new_relic/agent/transaction/message_broker_segment'
10
+
11
+ module NewRelic
12
+ module Agent
13
+ #
14
+ # This class helps you interact with the current transaction (if
15
+ # it exists), start new transactions/segments, etc.
16
+ #
17
+ # @api public
18
+ class Tracer
19
+ class << self
20
+ def state
21
+ state_for(Thread.current)
22
+ end
23
+
24
+ alias_method :tl_get, :state
25
+
26
+ # Returns +true+ unless called from within an
27
+ # +NewRelic::Agent.disable_all_tracing+ block.
28
+ #
29
+ # @api public
30
+ def tracing_enabled?
31
+ state.tracing_enabled?
32
+ end
33
+
34
+ # Returns the transaction in progress for this thread, or
35
+ # +nil+ if none exists.
36
+ #
37
+ # @api public
38
+ def current_transaction
39
+ state.current_transaction
40
+ end
41
+
42
+ # Runs the given block of code in a transaction.
43
+ #
44
+ # @param [String] name reserved for New Relic internal use
45
+ #
46
+ # @param [String] partial_name a meaningful name for this
47
+ # transaction (e.g., +blogs/index+); the Ruby agent will add a
48
+ # New-Relic-specific prefix
49
+ #
50
+ # @param [Symbol] category +:web+ for web transactions or
51
+ # +:background+ for background transactions
52
+ #
53
+ # @param [Hash] options reserved for New Relic internal use
54
+ #
55
+ # @api public
56
+ def in_transaction(name: nil,
57
+ partial_name: nil,
58
+ category: nil,
59
+ options: {})
60
+
61
+ finishable = start_transaction_or_segment(
62
+ name: name,
63
+ partial_name: partial_name,
64
+ category: category,
65
+ options: options
66
+ )
67
+
68
+ begin
69
+ # We shouldn't raise from Tracer.start_transaction_or_segment, but
70
+ # only wrap the yield to be absolutely sure we don't report agent
71
+ # problems as app errors
72
+ yield
73
+ rescue => e
74
+ current_transaction.notice_error(e)
75
+ raise e
76
+ ensure
77
+ finishable.finish if finishable
78
+ end
79
+ end
80
+
81
+ # Starts a segment on the current transaction (if one exists)
82
+ # or starts a new transaction otherwise.
83
+ #
84
+ # @param [String] name reserved for New Relic internal use
85
+ #
86
+ # @param [String] partial_name a meaningful name for this
87
+ # transaction (e.g., +blogs/index+); the Ruby agent will add a
88
+ # New-Relic-specific prefix
89
+ #
90
+ # @param [Symbol] category +:web+ for web transactions or
91
+ # +:background+ for background transactions
92
+ #
93
+ # @param [Hash] options reserved for New Relic internal use
94
+ #
95
+ # @return [Object, #finish] an object that responds to
96
+ # +finish+; you _must_ call +finish+ on it at the end of the
97
+ # code you're tracing
98
+ #
99
+ # @api public
100
+ def start_transaction_or_segment(name: nil,
101
+ partial_name: nil,
102
+ category: nil,
103
+ options: {})
104
+ if name.nil? && partial_name.nil?
105
+ raise ArgumentError, 'missing required argument: name or partial_name'
106
+ end
107
+ if category.nil?
108
+ raise ArgumentError, 'missing required argument: category'
109
+ end
110
+
111
+ if name
112
+ options[:transaction_name] = name
113
+ else
114
+ options[:transaction_name] = Transaction.name_from_partial(
115
+ partial_name,
116
+ category
117
+ )
118
+ end
119
+
120
+ if (txn = current_transaction)
121
+ txn.create_nested_segment(category, options)
122
+ else
123
+ Transaction.start_new_transaction(state, category, options)
124
+ end
125
+
126
+ rescue ArgumentError
127
+ raise
128
+ rescue => e
129
+ log_error('start_transaction_or_segment', e)
130
+ end
131
+
132
+ # Takes name or partial_name and a category.
133
+ # Returns a transaction instance or nil
134
+ def start_transaction(name: nil,
135
+ partial_name: nil,
136
+ category: nil,
137
+ **options)
138
+ if name.nil? && partial_name.nil?
139
+ raise ArgumentError, 'missing required argument: name or partial_name'
140
+ end
141
+ if category.nil?
142
+ raise ArgumentError, 'missing required argument: category'
143
+ end
144
+
145
+ return current_transaction if current_transaction
146
+
147
+ if name
148
+ options[:transaction_name] = name
149
+ else
150
+ options[:transaction_name] = Transaction.name_from_partial(
151
+ partial_name,
152
+ category
153
+ )
154
+ end
155
+
156
+ Transaction.start_new_transaction(state,
157
+ category,
158
+ options)
159
+ rescue ArgumentError
160
+ raise
161
+ rescue => e
162
+ log_error('start_transaction', e)
163
+ end
164
+
165
+ def create_distributed_trace_payload
166
+ return unless txn = current_transaction
167
+ txn.create_distributed_trace_payload
168
+ end
169
+
170
+ def accept_distributed_trace_payload(payload)
171
+ return unless txn = current_transaction
172
+ txn.accept_distributed_trace_payload(payload)
173
+ end
174
+
175
+ # Returns the currently active segment in the transaction in
176
+ # progress for this thread, or +nil+ if no segment or
177
+ # transaction exists.
178
+ #
179
+ # @api public
180
+ def current_segment
181
+ return unless txn = current_transaction
182
+ txn.current_segment
183
+ end
184
+
185
+ # Creates and starts a general-purpose segment used to time
186
+ # arbitrary code.
187
+ #
188
+ # @param [String] name full name of the segment; the agent
189
+ # will not add a prefix. Third-party users should begin the
190
+ # name with +Custom/+; e.g.,
191
+ # +Custom/UserMailer/send_welcome_email+
192
+ #
193
+ # @param [optional, String, Array] unscoped_metrics additional
194
+ # unscoped metrics to record using this segment's timing
195
+ # information
196
+ #
197
+ # @param start_time [optional, Time] a +Time+ instance
198
+ # denoting the start time of the segment. Value is set by
199
+ # AbstractSegment#start if not given.
200
+ #
201
+ # @param parent [optional, Segment] Use for the rare cases
202
+ # (such as async) where the parent segment should be something
203
+ # other than the current segment
204
+ #
205
+ # @return [Segment] the newly created segment; you _must_ call
206
+ # +finish+ on it at the end of the code you're tracing
207
+ #
208
+ # @api public
209
+ def start_segment(name:nil,
210
+ unscoped_metrics:nil,
211
+ start_time: nil,
212
+ parent: nil)
213
+
214
+ # ruby 2.0.0 does not support required kwargs
215
+ raise ArgumentError, 'missing required argument: name' if name.nil?
216
+
217
+ segment = Transaction::Segment.new name, unscoped_metrics, start_time
218
+ start_and_add_segment segment, parent
219
+
220
+ rescue ArgumentError
221
+ raise
222
+ rescue => e
223
+ log_error('start_segment', e)
224
+ end
225
+
226
+ UNKNOWN = "Unknown".freeze
227
+ OTHER = "other".freeze
228
+
229
+ # Creates and starts a datastore segment used to time
230
+ # datastore operations.
231
+ #
232
+ # @param [String] product the datastore name for use in metric
233
+ # naming, e.g. "FauxDB"
234
+ #
235
+ # @param [String] operation the name of the operation
236
+ # (e.g. "select"), often named after the method that's being
237
+ # instrumented.
238
+ #
239
+ # @param [optional, String] collection the collection name for use in
240
+ # statement-level metrics (i.e. table or model name)
241
+ #
242
+ # @param [optional, String] host the host this database
243
+ # instance is running on
244
+ #
245
+ # @param [optional, String] port_path_or_id TCP port, file
246
+ # path, UNIX domain socket, or other connection-related info
247
+ #
248
+ # @param [optional, String] database_name the name of this
249
+ # database
250
+ #
251
+ # @param start_time [optional, Time] a +Time+ instance
252
+ # denoting the start time of the segment. Value is set by
253
+ # AbstractSegment#start if not given.
254
+ #
255
+ # @param parent [optional, Segment] Use for the rare cases
256
+ # (such as async) where the parent segment should be something
257
+ # other than the current segment
258
+ #
259
+ # @return [DatastoreSegment] the newly created segment; you
260
+ # _must_ call +finish+ on it at the end of the code you're
261
+ # tracing
262
+ #
263
+ # @api public
264
+ def start_datastore_segment(product: nil,
265
+ operation: nil,
266
+ collection: nil,
267
+ host: nil,
268
+ port_path_or_id: nil,
269
+ database_name: nil,
270
+ start_time: nil,
271
+ parent: nil)
272
+
273
+ product ||= UNKNOWN
274
+ operation ||= OTHER
275
+
276
+ segment = Transaction::DatastoreSegment.new product, operation, collection, host, port_path_or_id, database_name
277
+ start_and_add_segment segment, parent
278
+
279
+ rescue ArgumentError
280
+ raise
281
+ rescue => e
282
+ log_error('start_datastore_segment', e)
283
+ end
284
+
285
+ # Creates and starts an external request segment using the
286
+ # given library, URI, and procedure. This is used to time
287
+ # external calls made over HTTP.
288
+ #
289
+ # @param [String] library a string of the class name of the library used to
290
+ # make the external call, for example, 'Net::HTTP'.
291
+ #
292
+ # @param [String, URI] uri indicates the URI to which the
293
+ # external request is being made. The URI should begin with the protocol,
294
+ # for example, 'https://github.com'.
295
+ #
296
+ # @param [String] procedure the HTTP method being used for the external
297
+ # request as a string, for example, 'GET'.
298
+ #
299
+ # @param start_time [optional, Time] a +Time+ instance
300
+ # denoting the start time of the segment. Value is set by
301
+ # AbstractSegment#start if not given.
302
+ #
303
+ # @param parent [optional, Segment] Use for the rare cases
304
+ # (such as async) where the parent segment should be something
305
+ # other than the current segment
306
+ #
307
+ # @return [ExternalRequestSegment] the newly created segment;
308
+ # you _must_ call +finish+ on it at the end of the code
309
+ # you're tracing
310
+ #
311
+ # @api public
312
+ def start_external_request_segment(library: nil,
313
+ uri: nil,
314
+ procedure: nil,
315
+ start_time: nil,
316
+ parent: nil)
317
+
318
+ # ruby 2.0.0 does not support required kwargs
319
+ raise ArgumentError, 'missing required argument: library' if library.nil?
320
+ raise ArgumentError, 'missing required argument: uri' if uri.nil?
321
+ raise ArgumentError, 'missing required argument: procedure' if procedure.nil?
322
+
323
+ segment = Transaction::ExternalRequestSegment.new library, uri, procedure, start_time
324
+ start_and_add_segment segment, parent
325
+
326
+ rescue ArgumentError
327
+ raise
328
+ rescue => e
329
+ log_error('start_external_request_segment', e)
330
+ end
331
+
332
+ # For New Relic internal use only.
333
+ def start_message_broker_segment(action: nil,
334
+ library: nil,
335
+ destination_type: nil,
336
+ destination_name: nil,
337
+ headers: nil,
338
+ parameters: nil,
339
+ start_time: nil,
340
+ parent: nil)
341
+
342
+ # ruby 2.0.0 does not support required kwargs
343
+ raise ArgumentError, 'missing required argument: action' if action.nil?
344
+ raise ArgumentError, 'missing required argument: library' if library.nil?
345
+ raise ArgumentError, 'missing required argument: destination_type' if destination_type.nil?
346
+ raise ArgumentError, 'missing required argument: destination_name' if destination_name.nil?
347
+
348
+ segment = Transaction::MessageBrokerSegment.new(
349
+ action: action,
350
+ library: library,
351
+ destination_type: destination_type,
352
+ destination_name: destination_name,
353
+ headers: headers,
354
+ parameters: parameters,
355
+ start_time: start_time
356
+ )
357
+ start_and_add_segment segment, parent
358
+
359
+ rescue ArgumentError
360
+ raise
361
+ rescue => e
362
+ log_error('start_datastore_segment', e)
363
+ end
364
+
365
+ # This method should only be used by Tracer for access to the
366
+ # current thread's state or to provide read-only accessors for other threads
367
+ #
368
+ # If ever exposed, this requires additional synchronization
369
+ def state_for(thread)
370
+ state = thread[:newrelic_tracer_state]
371
+
372
+ if state.nil?
373
+ state = Tracer::State.new
374
+ thread[:newrelic_tracer_state] = state
375
+ end
376
+
377
+ state
378
+ end
379
+
380
+ alias_method :tl_state_for, :state_for
381
+
382
+ def clear_state
383
+ Thread.current[:newrelic_tracer_state] = nil
384
+ end
385
+
386
+ alias_method :tl_clear, :clear_state
387
+
388
+ private
389
+
390
+ def start_and_add_segment segment, parent = nil
391
+ tracer_state = state
392
+ if (txn = tracer_state.current_transaction) &&
393
+ tracer_state.tracing_enabled?
394
+ txn.add_segment segment, parent
395
+ else
396
+ segment.record_metrics = false
397
+ end
398
+ segment.start
399
+ segment
400
+ end
401
+
402
+ def log_error(method_name, exception)
403
+ NewRelic::Agent.logger.error("Exception during Tracer.#{method_name}", exception)
404
+ nil
405
+ end
406
+ end
407
+
408
+ # This is THE location to store thread local information during a transaction
409
+ # Need a new piece of data? Add a method here, NOT a new thread local variable.
410
+ class State
411
+
412
+ def initialize
413
+ @untraced = []
414
+ @current_transaction = nil
415
+ @record_sql = nil
416
+ end
417
+
418
+ # This starts the timer for the transaction.
419
+ def reset(transaction=nil)
420
+ # We purposefully don't reset @untraced or @record_sql
421
+ # since those are managed by NewRelic::Agent.disable_* calls explicitly
422
+ # and (more importantly) outside the scope of a transaction
423
+
424
+ @current_transaction = transaction
425
+ @sql_sampler_transaction_data = nil
426
+ end
427
+
428
+ # Current transaction stack
429
+ attr_reader :current_transaction
430
+
431
+ # Execution tracing on current thread
432
+ attr_accessor :untraced
433
+
434
+ def push_traced(should_trace)
435
+ @untraced << should_trace
436
+ end
437
+
438
+ def pop_traced
439
+ @untraced.pop if @untraced
440
+ end
441
+
442
+ def is_execution_traced?
443
+ @untraced.nil? || @untraced.last != false
444
+ end
445
+
446
+ alias_method :tracing_enabled?, :is_execution_traced?
447
+
448
+ # TT's and SQL
449
+ attr_accessor :record_sql
450
+
451
+ def is_sql_recorded?
452
+ @record_sql != false
453
+ end
454
+
455
+ # Sql Sampler Transaction Data
456
+ attr_accessor :sql_sampler_transaction_data
457
+ end
458
+ end
459
+
460
+ TransactionState = Tracer
461
+ end
462
+ end