newrelic_rpm 3.1.0 → 3.1.1.beta1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of newrelic_rpm might be problematic. Click here for more details.

Files changed (94) hide show
  1. data/CHANGELOG +3 -0
  2. data/lib/new_relic/agent.rb +29 -12
  3. data/lib/new_relic/agent/agent.rb +355 -78
  4. data/lib/new_relic/agent/beacon_configuration.rb +49 -7
  5. data/lib/new_relic/agent/browser_monitoring.rb +20 -1
  6. data/lib/new_relic/agent/busy_calculator.rb +11 -3
  7. data/lib/new_relic/agent/chained_call.rb +2 -2
  8. data/lib/new_relic/agent/error_collector.rb +229 -183
  9. data/lib/new_relic/agent/instrumentation.rb +2 -2
  10. data/lib/new_relic/agent/instrumentation/active_merchant.rb +5 -1
  11. data/lib/new_relic/agent/instrumentation/acts_as_solr.rb +5 -1
  12. data/lib/new_relic/agent/instrumentation/authlogic.rb +4 -0
  13. data/lib/new_relic/agent/instrumentation/controller_instrumentation.rb +16 -5
  14. data/lib/new_relic/agent/instrumentation/data_mapper.rb +5 -1
  15. data/lib/new_relic/agent/instrumentation/delayed_job_instrumentation.rb +5 -1
  16. data/lib/new_relic/agent/instrumentation/memcache.rb +5 -1
  17. data/lib/new_relic/agent/instrumentation/merb/controller.rb +5 -1
  18. data/lib/new_relic/agent/instrumentation/merb/errors.rb +5 -1
  19. data/lib/new_relic/agent/instrumentation/metric_frame/pop.rb +0 -5
  20. data/lib/new_relic/agent/instrumentation/net.rb +5 -1
  21. data/lib/new_relic/agent/instrumentation/rails/action_controller.rb +7 -3
  22. data/lib/new_relic/agent/instrumentation/rails/action_web_service.rb +5 -1
  23. data/lib/new_relic/agent/instrumentation/rails/active_record_instrumentation.rb +5 -1
  24. data/lib/new_relic/agent/instrumentation/rails/errors.rb +4 -0
  25. data/lib/new_relic/agent/instrumentation/rails3/action_controller.rb +4 -0
  26. data/lib/new_relic/agent/instrumentation/rails3/active_record_instrumentation.rb +13 -6
  27. data/lib/new_relic/agent/instrumentation/rails3/errors.rb +4 -0
  28. data/lib/new_relic/agent/instrumentation/sinatra.rb +4 -0
  29. data/lib/new_relic/agent/instrumentation/sunspot.rb +4 -0
  30. data/lib/new_relic/agent/instrumentation/unicorn_instrumentation.rb +5 -1
  31. data/lib/new_relic/agent/method_tracer.rb +205 -99
  32. data/lib/new_relic/agent/shim_agent.rb +0 -1
  33. data/lib/new_relic/agent/stats_engine.rb +1 -0
  34. data/lib/new_relic/agent/stats_engine/metric_stats.rb +23 -7
  35. data/lib/new_relic/agent/stats_engine/samplers.rb +8 -2
  36. data/lib/new_relic/agent/stats_engine/transactions.rb +26 -12
  37. data/lib/new_relic/agent/transaction_sampler.rb +3 -1
  38. data/lib/new_relic/agent/worker_loop.rb +13 -5
  39. data/lib/new_relic/collection_helper.rb +6 -3
  40. data/lib/new_relic/control.rb +1 -3
  41. data/lib/new_relic/control/class_methods.rb +8 -3
  42. data/lib/new_relic/control/configuration.rb +24 -5
  43. data/lib/new_relic/control/frameworks.rb +10 -0
  44. data/lib/new_relic/control/frameworks/external.rb +4 -4
  45. data/lib/new_relic/control/frameworks/merb.rb +1 -0
  46. data/lib/new_relic/control/frameworks/rails.rb +5 -5
  47. data/lib/new_relic/control/frameworks/rails3.rb +5 -3
  48. data/lib/new_relic/control/frameworks/ruby.rb +5 -5
  49. data/lib/new_relic/control/frameworks/sinatra.rb +1 -4
  50. data/lib/new_relic/control/instance_methods.rb +23 -7
  51. data/lib/new_relic/control/instrumentation.rb +22 -3
  52. data/lib/new_relic/control/logging_methods.rb +25 -7
  53. data/lib/new_relic/control/server_methods.rb +16 -6
  54. data/lib/new_relic/data_serialization.rb +83 -14
  55. data/lib/new_relic/delayed_job_injection.rb +7 -1
  56. data/lib/new_relic/local_environment.rb +55 -25
  57. data/lib/new_relic/metric_data.rb +7 -2
  58. data/lib/new_relic/metric_spec.rb +5 -3
  59. data/lib/new_relic/stats.rb +16 -7
  60. data/lib/new_relic/transaction_analysis.rb +2 -1
  61. data/lib/new_relic/transaction_analysis/segment_summary.rb +4 -2
  62. data/lib/new_relic/transaction_sample.rb +33 -7
  63. data/lib/new_relic/transaction_sample/segment.rb +21 -3
  64. data/lib/new_relic/version.rb +2 -2
  65. data/newrelic_rpm.gemspec +7 -11
  66. data/test/config/newrelic.yml +1 -1
  67. data/test/new_relic/agent/agent/start_worker_thread_test.rb +1 -4
  68. data/test/new_relic/agent/agent_test.rb +16 -0
  69. data/test/new_relic/agent/agent_test_controller.rb +1 -1
  70. data/test/new_relic/agent/agent_test_controller_test.rb +14 -19
  71. data/test/new_relic/agent/beacon_configuration_test.rb +2 -2
  72. data/test/new_relic/agent/browser_monitoring_test.rb +7 -3
  73. data/test/new_relic/agent/instrumentation/active_record_instrumentation_test.rb +13 -4
  74. data/test/new_relic/agent/instrumentation/metric_frame/pop_test.rb +0 -10
  75. data/test/new_relic/agent/method_tracer/instance_methods/trace_execution_scoped_test.rb +1 -1
  76. data/test/new_relic/agent_test.rb +168 -0
  77. data/test/new_relic/collection_helper_test.rb +21 -3
  78. data/test/new_relic/control/configuration_test.rb +25 -0
  79. data/test/new_relic/data_serialization_test.rb +58 -3
  80. data/test/new_relic/delayed_job_injection_test.rb +17 -0
  81. data/test/new_relic/transaction_analysis/segment_summary_test.rb +14 -0
  82. data/test/new_relic/transaction_analysis_test.rb +3 -3
  83. data/test/new_relic/transaction_sample/segment_test.rb +11 -0
  84. data/test/test_helper.rb +1 -1
  85. data/vendor/gems/dependency_detection-0.0.1.build/LICENSE +4 -18
  86. metadata +13 -13
  87. data/lib/new_relic/histogram.rb +0 -91
  88. data/lib/new_relic/rack/metric_app.rb +0 -65
  89. data/lib/new_relic/rack/mongrel_rpm.ru +0 -28
  90. data/lib/new_relic/rack/newrelic.yml +0 -27
  91. data/lib/new_relic/rack_app.rb +0 -6
  92. data/vendor/gems/dependency_detection-0.0.1.build/README +0 -0
  93. data/vendor/gems/metric_parser-0.1.0.pre1/LICENSE +0 -0
  94. data/vendor/gems/metric_parser-0.1.0.pre1/README +0 -0
data/CHANGELOG CHANGED
@@ -1,3 +1,6 @@
1
+ v3.1.1
2
+ * Support for Rails 3.1 (thanks to Ben Hoskings via github)
3
+
1
4
  v3.1.0
2
5
  * Support for aggregating data from short-running
3
6
  processes to reduce reporting overhead
@@ -70,7 +70,6 @@ module NewRelic
70
70
  require 'new_relic/transaction_sample'
71
71
  require 'new_relic/url_rule'
72
72
  require 'new_relic/noticed_error'
73
- require 'new_relic/histogram'
74
73
  require 'new_relic/timer_lib'
75
74
 
76
75
  require 'new_relic/agent'
@@ -135,13 +134,13 @@ module NewRelic
135
134
  # Get or create a statistics gatherer that will aggregate numerical data
136
135
  # under a metric name.
137
136
  #
138
- # +metric_name+ should follow a slash separated path convention. Application
137
+ # +metric_name+ should follow a slash separated path convention. Application
139
138
  # specific metrics should begin with "Custom/".
140
139
  #
141
140
  # Return a NewRelic::Stats that accepts data
142
141
  # via calls to add_data_point(value).
143
142
  def get_stats(metric_name, use_scope=false)
144
- @agent.stats_engine.get_stats(metric_name, use_scope)
143
+ agent.stats_engine.get_stats(metric_name, use_scope)
145
144
  end
146
145
 
147
146
  alias get_stats_no_scope get_stats
@@ -198,24 +197,40 @@ module NewRelic
198
197
  agent.after_fork(options)
199
198
  end
200
199
 
201
- # Clear out any unsent metric data.
200
+ # Clear out any unsent metric data. See NewRelic::Agent::Agent#reset_stats
202
201
  def reset_stats
203
202
  agent.reset_stats
204
203
  end
205
204
 
206
205
  # Shutdown the agent. Call this before exiting. Sends any queued data
207
206
  # and kills the background thread.
208
- def shutdown(options = {})
207
+ def shutdown(options={})
209
208
  agent.shutdown(options)
210
209
  end
211
-
210
+
211
+ # a method used to serialize short-running processes to disk, so
212
+ # we don't incur the overhead of reporting to the server for every
213
+ # fork/invocation of a small job.
214
+ #
215
+ # Functionally, this loads the data from the file into the agent
216
+ # (to avoid losing data by overwriting) and then serializes the
217
+ # agent data to the file again. See also #load_data
212
218
  def save_data
213
219
  NewRelic::DataSerialization.read_and_write_to_file do |old_data|
214
220
  agent.merge_data_from(old_data)
215
221
  agent.serialize
216
222
  end
217
223
  end
224
+
225
+ # used to load data from the disk during the harvest cycle to send
226
+ # it. This method also clears the file so data should never be
227
+ # sent more than once.
218
228
 
229
+ # Note that only one transaction trace will be sent even if many
230
+ # are serialized, since the slowest is sent.
231
+ #
232
+ # See also the complement to this method, #save_data - used when a
233
+ # process is shutting down
219
234
  def load_data
220
235
  value = nil
221
236
  NewRelic::DataSerialization.read_and_write_to_file do |old_data|
@@ -223,6 +238,7 @@ module NewRelic
223
238
  value = {:metrics => agent.stats_engine.metrics.length, :traces => agent.unsent_traces_size, :errors => agent.unsent_errors_size}
224
239
  nil # return nil so nothing is written to the file
225
240
  end
241
+ NewRelic::DataSerialization.update_last_sent!
226
242
  value
227
243
  end
228
244
 
@@ -288,10 +304,7 @@ module NewRelic
288
304
  # any. Only affects the transaction started on this thread once
289
305
  # it has started and before it has completed.
290
306
  def abort_transaction!
291
- # The class may not be loaded if the agent is disabled
292
- if defined? NewRelic::Agent::Instrumentation::MetricFrame
293
- NewRelic::Agent::Instrumentation::MetricFrame.abort_transaction!
294
- end
307
+ NewRelic::Agent::Instrumentation::MetricFrame.abort_transaction!
295
308
  end
296
309
 
297
310
  # Yield to the block without collecting any metrics or traces in
@@ -310,11 +323,15 @@ module NewRelic
310
323
  def is_execution_traced?
311
324
  Thread.current[:newrelic_untraced].nil? || Thread.current[:newrelic_untraced].last != false
312
325
  end
313
-
326
+
327
+ # helper method to check the thread local to determine whether the
328
+ # transaction in progress is traced or not
314
329
  def is_transaction_traced?
315
330
  Thread::current[:record_tt] != false
316
331
  end
317
-
332
+
333
+ # helper method to check the thread local to determine whether sql
334
+ # is being recorded or not
318
335
  def is_sql_recorded?
319
336
  Thread::current[:record_sql] != false
320
337
  end
@@ -33,7 +33,6 @@ module NewRelic
33
33
  @launch_time = Time.now
34
34
 
35
35
  @metric_ids = {}
36
- @histogram = NewRelic::Histogram.new(NewRelic::Control.instance.apdex_t / 10)
37
36
  @stats_engine = NewRelic::Agent::StatsEngine.new
38
37
  @transaction_sampler = NewRelic::Agent::TransactionSampler.new
39
38
  @stats_engine.transaction_sampler = @transaction_sampler
@@ -45,39 +44,67 @@ module NewRelic
45
44
  @last_harvest_time = Time.now
46
45
  @obfuscator = method(:default_sql_obfuscator)
47
46
  end
48
-
47
+
48
+ # contains all the class-level methods for NewRelic::Agent::Agent
49
49
  module ClassMethods
50
- # Should only be called by NewRelic::Control
50
+ # Should only be called by NewRelic::Control - returns a
51
+ # memoized singleton instance of the agent, creating one if needed
51
52
  def instance
52
53
  @instance ||= self.new
53
54
  end
54
55
  end
55
-
56
+
57
+ # Holds all the methods defined on NewRelic::Agent::Agent
58
+ # instances
56
59
  module InstanceMethods
57
-
60
+
61
+ # holds a proc that is used to obfuscate sql statements
58
62
  attr_reader :obfuscator
63
+ # the statistics engine that holds all the timeslice data
59
64
  attr_reader :stats_engine
65
+ # the transaction sampler that handles recording transactions
60
66
  attr_reader :transaction_sampler
67
+ # error collector is a simple collection of recorded errors
61
68
  attr_reader :error_collector
69
+ # whether we should record raw, obfuscated, or no sql
62
70
  attr_reader :record_sql
63
- attr_reader :histogram
71
+ # a cached set of metric_ids to save the collector some time -
72
+ # it returns a metric id for every metric name we send it, and
73
+ # in the future we transmit using the metric id only
64
74
  attr_reader :metric_ids
75
+ # in theory a set of rules applied by the agent to the output
76
+ # of its metrics. Currently unimplemented
65
77
  attr_reader :url_rules
78
+ # a configuration for the Real User Monitoring system -
79
+ # handles things like static setup of the header for inclusion
80
+ # into pages
66
81
  attr_reader :beacon_configuration
67
-
82
+
83
+ # Returns the length of the unsent errors array, if it exists,
84
+ # otherwise nil
68
85
  def unsent_errors_size
69
86
  @unsent_errors.length if @unsent_errors
70
87
  end
71
-
88
+
89
+ # Returns the length of the traces array, if it exists,
90
+ # otherwise nil
72
91
  def unsent_traces_size
73
92
  @traces.length if @traces
74
93
  end
75
-
94
+
95
+ # Initializes the unsent timeslice data hash, if needed, and
96
+ # returns the number of keys it contains
76
97
  def unsent_timeslice_data
77
98
  @unsent_timeslice_data ||= {}
78
99
  @unsent_timeslice_data.keys.length
79
100
  end
80
101
 
102
+ # fakes out a transaction that did not happen in this process
103
+ # by creating apdex, summary metrics, and recording statistics
104
+ # for the transaction
105
+ #
106
+ # This method is *deprecated* - it may be removed in future
107
+ # versions of the agent
81
108
  def record_transaction(duration_seconds, options={})
82
109
  is_error = options['is_error'] || options['error_message'] || options['exception']
83
110
  metric = options['metric']
@@ -87,7 +114,6 @@ module NewRelic
87
114
 
88
115
  if metric_info.is_web_transaction?
89
116
  NewRelic::Agent::Instrumentation::MetricFrame.record_apdex(metric_info, duration_seconds, duration_seconds, is_error)
90
- histogram.process(duration_seconds)
91
117
  end
92
118
  metrics = metric_info.summary_metrics
93
119
 
@@ -163,7 +189,12 @@ module NewRelic
163
189
  @connected
164
190
  end
165
191
 
166
- # Attempt a graceful shutdown of the agent.
192
+ # Attempt a graceful shutdown of the agent, running the worker
193
+ # loop if it exists and is running.
194
+ #
195
+ # Options:
196
+ # :force_send => (true/false) # force the agent to send data
197
+ # before shutting down
167
198
  def shutdown(options={})
168
199
  run_loop_before_exit = options.fetch(:force_send, false)
169
200
  return if not started?
@@ -193,27 +224,38 @@ module NewRelic
193
224
  @started = nil
194
225
  end
195
226
 
227
+ # Tells the statistics engine we are starting a new transaction
196
228
  def start_transaction
197
229
  @stats_engine.start_transaction
198
230
  end
199
231
 
232
+ # Tells the statistics engine we are ending a transaction
200
233
  def end_transaction
201
234
  @stats_engine.end_transaction
202
235
  end
203
236
 
237
+ # Sets a thread local variable as to whether we should or
238
+ # should not record sql in the current thread. Returns the
239
+ # previous value, if there is one
204
240
  def set_record_sql(should_record)
205
241
  prev = Thread::current[:record_sql]
206
242
  Thread::current[:record_sql] = should_record
207
243
  prev.nil? || prev
208
244
  end
209
245
 
246
+ # Sets a thread local variable as to whether we should or
247
+ # should not record transaction traces in the current
248
+ # thread. Returns the previous value, if there is one
210
249
  def set_record_tt(should_record)
211
250
  prev = Thread::current[:record_tt]
212
251
  Thread::current[:record_tt] = should_record
213
252
  prev.nil? || prev
214
253
  end
254
+
215
255
  # Push flag indicating whether we should be tracing in this
216
- # thread.
256
+ # thread. This uses a stack which allows us to disable tracing
257
+ # children of a transaction without affecting the tracing of
258
+ # the whole transaction
217
259
  def push_trace_execution_flag(should_trace=false)
218
260
  value = Thread.current[:newrelic_untraced]
219
261
  if (value.nil?)
@@ -229,6 +271,17 @@ module NewRelic
229
271
  Thread.current[:newrelic_untraced].pop if Thread.current[:newrelic_untraced]
230
272
  end
231
273
 
274
+ # Sets the sql obfuscator used to clean up sql when sending it
275
+ # to the server. Possible types are:
276
+ #
277
+ # :before => sets the block to run before the existing
278
+ # obfuscators
279
+ #
280
+ # :after => sets the block to run after the existing
281
+ # obfuscator(s)
282
+ #
283
+ # :replace => removes the current obfuscator and replaces it
284
+ # with the provided block
232
285
  def set_sql_obfuscator(type, &block)
233
286
  if type == :before
234
287
  @obfuscator = NewRelic::ChainedCall.new(block, @obfuscator)
@@ -241,6 +294,7 @@ module NewRelic
241
294
  end
242
295
  end
243
296
 
297
+ # Shorthand to the NewRelic::Agent.logger method
244
298
  def log
245
299
  NewRelic::Agent.logger
246
300
  end
@@ -248,6 +302,7 @@ module NewRelic
248
302
  # Herein lies the corpse of the former 'start' method. May
249
303
  # it's unmatched flog score rest in pieces.
250
304
  module Start
305
+ # Check whether we have already started, which is an error condition
251
306
  def already_started?
252
307
  if started?
253
308
  control.log!("Agent Started Already!", :error)
@@ -255,28 +310,44 @@ module NewRelic
255
310
  end
256
311
  end
257
312
 
313
+ # The agent is disabled when it is not force enabled by the
314
+ # 'agent_enabled' option (e.g. in a manual start), or
315
+ # enabled normally through the configuration file
258
316
  def disabled?
259
317
  !control.agent_enabled?
260
318
  end
261
319
 
320
+ # Logs the dispatcher to the log file to assist with
321
+ # debugging. When no debugger is present, logs this fact to
322
+ # assist with proper dispatcher detection
262
323
  def log_dispatcher
263
324
  dispatcher_name = control.dispatcher.to_s
264
325
  return if log_if(dispatcher_name.empty?, :info, "No dispatcher detected.")
265
326
  log.info "Dispatcher: #{dispatcher_name}"
266
327
  end
267
328
 
329
+ # Logs the configured application names
268
330
  def log_app_names
269
331
  log.info "Application: #{control.app_names.join(", ")}"
270
332
  end
271
-
333
+
334
+ # apdex_f is always 4 times the apdex_t
272
335
  def apdex_f
273
336
  (4 * NewRelic::Control.instance.apdex_t).to_f
274
337
  end
275
338
 
339
+ # If the transaction threshold is set to the string
340
+ # 'apdex_f', we use 4 times the apdex_t value to record
341
+ # transactions. This gears well with using apdex since you
342
+ # will attempt to send any transactions that register as 'failing'
276
343
  def apdex_f_threshold?
277
344
  sampler_config.fetch('transaction_threshold', '') =~ /apdex_f/i
278
345
  end
279
346
 
347
+ # Sets the sql recording configuration by trying to detect
348
+ # any attempt to disable the sql collection - 'off',
349
+ # 'false', 'none', and friends. Otherwise, we accept 'raw',
350
+ # and unrecognized values default to 'obfuscated'
280
351
  def set_sql_recording!
281
352
  record_sql_config = sampler_config.fetch('record_sql', :obfuscated)
282
353
  case record_sql_config.to_s
@@ -295,10 +366,13 @@ module NewRelic
295
366
  log_sql_transmission_warning?
296
367
  end
297
368
 
369
+ # Warn the user when we are sending raw sql across the wire
370
+ # - they should probably be using ssl when this is true
298
371
  def log_sql_transmission_warning?
299
372
  log_if((@record_sql == :raw), :warn, "Agent is configured to send raw SQL to the service")
300
373
  end
301
374
 
375
+ # gets the sampler configuration from the control object's settings
302
376
  def sampler_config
303
377
  control.fetch('transaction_tracer', {})
304
378
  end
@@ -319,18 +393,27 @@ module NewRelic
319
393
  @slowest_transaction_threshold = apdex_f if apdex_f_threshold?
320
394
  end
321
395
 
396
+ # Connecting in the foreground blocks further startup of the
397
+ # agent until we have a connection - useful in cases where
398
+ # you're trying to log a very-short-running process and want
399
+ # to get statistics from before a server connection
400
+ # (typically 20 seconds) exists
322
401
  def connect_in_foreground
323
402
  NewRelic::Agent.disable_all_tracing { connect(:keep_retrying => false) }
324
403
  end
325
404
 
405
+ # Are we in boss mode, using rubinius?
326
406
  def using_rubinius?
327
407
  RUBY_VERSION =~ /rubinius/i
328
408
  end
329
409
 
410
+ # Is this really a world-within-a-world, running JRuby?
330
411
  def using_jruby?
331
412
  defined?(JRuby)
332
413
  end
333
414
 
415
+ # If we're using sinatra, old versions run in an at_exit
416
+ # block so we should probably know that
334
417
  def using_sinatra?
335
418
  defined?(Sinatra::Application)
336
419
  end
@@ -341,6 +424,9 @@ module NewRelic
341
424
  using_rubinius? || using_jruby? || using_sinatra?
342
425
  end
343
426
 
427
+ # Installs our exit handler, which exploits the weird
428
+ # behavior of at_exit blocks to make sure it runs last, by
429
+ # doing an at_exit within an at_exit block.
344
430
  def install_exit_handler
345
431
  if control.send_data_on_exit && !weird_ruby?
346
432
  # Our shutdown handler needs to run after other shutdown handlers
@@ -348,46 +434,72 @@ module NewRelic
348
434
  end
349
435
  end
350
436
 
437
+ # Tells us in the log file where the log file is
438
+ # located. This seems redundant, but can come in handy when
439
+ # we have some log file path set by the user which parses
440
+ # incorrectly, sending the log file to who-knows-where
351
441
  def notify_log_file_location
352
442
  log_file = NewRelic::Control.instance.log_file
353
443
  log_if(log_file, :info, "Agent Log found in #{log_file}")
354
444
  end
355
445
 
446
+ # Classy logging of the agent version and the current pid,
447
+ # so we can disambiguate processes in the log file and make
448
+ # sure they're running a reasonable version
356
449
  def log_version_and_pid
357
450
  log.info "New Relic Ruby Agent #{NewRelic::VERSION::STRING} Initialized: pid = #{$$}"
358
451
  end
359
452
 
453
+ # A helper method that logs a condition if that condition is
454
+ # true. Mentally cleaner than having every method set a
455
+ # local and log if it is true
360
456
  def log_if(boolean, level, message)
361
457
  self.log.send(level, message) if boolean
362
458
  boolean
363
459
  end
364
460
 
461
+ # A helper method that logs a condition unless that
462
+ # condition is true. Mentally cleaner than having every
463
+ # method set a local and log unless it is true
365
464
  def log_unless(boolean, level, message)
366
465
  self.log.send(level, message) unless boolean
367
466
  boolean
368
467
  end
369
468
 
469
+ # Warn the user if they have configured their agent not to
470
+ # send data, that way we can see this clearly in the log file
370
471
  def monitoring?
371
472
  log_unless(control.monitor_mode?, :warn, "Agent configured not to send data in this environment - edit newrelic.yml to change this")
372
473
  end
373
474
 
475
+ # Tell the user when the license key is missing so they can
476
+ # fix it by adding it to the file
374
477
  def has_license_key?
375
478
  log_unless(control.license_key, :error, "No license key found. Please edit your newrelic.yml file and insert your license key.")
376
479
  end
377
480
 
481
+ # A correct license key exists and is of the proper length
378
482
  def has_correct_license_key?
379
483
  has_license_key? && correct_license_length
380
484
  end
381
485
 
486
+ # A license key is an arbitrary 40 character string,
487
+ # usually looks something like a SHA1 hash
382
488
  def correct_license_length
383
489
  key = control.license_key
384
490
  log_unless((key.length == 40), :error, "Invalid license key: #{key}")
385
491
  end
386
492
 
493
+ # If we're using a dispatcher that forks before serving
494
+ # requests, we need to wait until the children are forked
495
+ # before connecting, otherwise the parent process sends odd data
387
496
  def using_forking_dispatcher?
388
497
  log_if([:passenger, :unicorn].include?(control.dispatcher), :info, "Connecting workers after forking.")
389
498
  end
390
499
 
500
+ # Sanity-check the agent configuration and start the agent,
501
+ # setting up the worker thread and the exit handler to shut
502
+ # down the agent
391
503
  def check_config_and_start_agent
392
504
  return unless monitoring? && has_correct_license_key?
393
505
  return if using_forking_dispatcher?
@@ -399,6 +511,7 @@ module NewRelic
399
511
 
400
512
  include Start
401
513
 
514
+ # Logs a bunch of data and starts the agent, if needed
402
515
  def start
403
516
  return if already_started? || disabled?
404
517
  @started = true
@@ -411,7 +524,8 @@ module NewRelic
411
524
  notify_log_file_location
412
525
  end
413
526
 
414
- # Clear out the metric data, errors, and transaction traces. Reset the histogram data.
527
+ # Clear out the metric data, errors, and transaction traces,
528
+ # making sure the agent is in a fresh state
415
529
  def reset_stats
416
530
  @stats_engine.reset_stats
417
531
  @unsent_errors = []
@@ -419,71 +533,82 @@ module NewRelic
419
533
  @unsent_timeslice_data = {}
420
534
  @last_harvest_time = Time.now
421
535
  @launch_time = Time.now
422
- @histogram = NewRelic::Histogram.new(NewRelic::Control.instance.apdex_t / 10)
423
536
  end
424
537
 
425
538
  private
426
539
  def collector
427
540
  @collector ||= control.server
428
541
  end
429
-
542
+
543
+ # All of this module used to be contained in the
544
+ # start_worker_thread method - this is an artifact of
545
+ # refactoring and can be moved, renamed, etc at will
430
546
  module StartWorkerThread
431
547
 
548
+ # disable transaction sampling if disabled by the server
549
+ # and we're not in dev mode
432
550
  def check_transaction_sampler_status
433
- # disable transaction sampling if disabled by the server
434
- # and we're not in dev mode
435
551
  if control.developer_mode? || @should_send_samples
436
552
  @transaction_sampler.enable
437
553
  else
438
554
  @transaction_sampler.disable
439
555
  end
440
556
  end
441
-
557
+
558
+ # logs info about the worker loop so users can see when the
559
+ # agent actually begins running in the background
442
560
  def log_worker_loop_start
443
561
  log.info "Reporting performance data every #{@report_period} seconds."
444
562
  log.debug "Running worker loop"
445
563
  end
446
564
 
565
+ # Creates the worker loop and loads it with the instructions
566
+ # it should run every @report_period seconds
447
567
  def create_and_run_worker_loop
448
568
  @worker_loop = WorkerLoop.new
449
569
  @worker_loop.run(@report_period) do
450
- NewRelic::Agent.load_data
451
- harvest_and_send_errors
452
- harvest_and_send_slowest_sample
453
- harvest_and_send_timeslice_data
570
+ save_or_transmit_data
454
571
  end
455
572
  end
456
573
 
574
+ # Handles the case where the server tells us to restart -
575
+ # this clears the data, clears connection attempts, and
576
+ # waits a while to reconnect.
457
577
  def handle_force_restart(error)
458
578
  log.info error.message
459
- # disconnect and start over.
460
- # clear the stats engine
461
579
  reset_stats
462
580
  @metric_ids = {}
463
581
  @connected = nil
464
- # Wait a short time before trying to reconnect
465
582
  sleep 30
466
583
  end
467
584
 
585
+ # when a disconnect is requested, stop the current thread, which
586
+ # is the worker thread that gathers data and talks to the
587
+ # server.
468
588
  def handle_force_disconnect(error)
469
- # when a disconnect is requested, stop the current thread, which
470
- # is the worker thread that gathers data and talks to the
471
- # server.
472
589
  log.error "New Relic forced this agent to disconnect (#{error.message})"
473
590
  disconnect
474
591
  end
475
592
 
593
+ # there is a problem with connecting to the server, so we
594
+ # stop trying to connect and shut down the agent
476
595
  def handle_server_connection_problem(error)
477
596
  log.error "Unable to establish connection with the server. Run with log level set to debug for more information."
478
597
  log.debug("#{error.class.name}: #{error.message}\n#{error.backtrace.first}")
479
598
  disconnect
480
599
  end
481
600
 
601
+ # Handles an unknown error in the worker thread by logging
602
+ # it and disconnecting the agent, since we are now in an
603
+ # unknown state
482
604
  def handle_other_error(error)
483
605
  log.error "Terminating worker loop: #{error.class.name}: #{error.message}\n #{error.backtrace.join("\n ")}"
484
606
  disconnect
485
607
  end
486
608
 
609
+ # a wrapper method to handle all the errors that can happen
610
+ # in the connection and worker thread system. This
611
+ # guarantees a no-throw from the background thread.
487
612
  def catch_errors
488
613
  yield
489
614
  rescue NewRelic::Agent::ForceRestartException => e
@@ -497,6 +622,14 @@ module NewRelic
497
622
  handle_other_error(e)
498
623
  end
499
624
 
625
+ # This is the method that is run in a new thread in order to
626
+ # background the harvesting and sending of data during the
627
+ # normal operation of the agent.
628
+ #
629
+ # Takes connection options that determine how we should
630
+ # connect to the server, and loops endlessly - typically we
631
+ # never return from this method unless we're shutting down
632
+ # the agent
500
633
  def deferred_work!(connection_options)
501
634
  catch_errors do
502
635
  NewRelic::Agent.disable_all_tracing do
@@ -509,6 +642,8 @@ module NewRelic
509
642
  check_transaction_sampler_status
510
643
  log_worker_loop_start
511
644
  create_and_run_worker_loop
645
+ # never reaches here unless there is a problem or
646
+ # the agent is exiting
512
647
  else
513
648
  log.debug "No connection. Worker thread ending."
514
649
  end
@@ -529,36 +664,59 @@ module NewRelic
529
664
  @worker_thread['newrelic_label'] = 'Worker Loop'
530
665
  end
531
666
 
667
+ # A shorthand for NewRelic::Control.instance
532
668
  def control
533
669
  NewRelic::Control.instance
534
670
  end
535
-
671
+
672
+ # This module is an artifact of a refactoring of the connect
673
+ # method - all of its methods are used in that context, so it
674
+ # can be refactored at will. It should be fully tested
536
675
  module Connect
676
+ # the frequency with which we should try to connect to the
677
+ # server at the moment.
537
678
  attr_accessor :connect_retry_period
679
+ # number of attempts we've made to contact the server
538
680
  attr_accessor :connect_attempts
539
681
 
682
+ # Disconnect just sets connected to false, which prevents
683
+ # the agent from trying to connect again
540
684
  def disconnect
541
685
  @connected = false
542
686
  true
543
687
  end
544
688
 
689
+ # We've tried to connect if @connected is not nil, or if we
690
+ # are forcing reconnection (i.e. in the case of an
691
+ # after_fork with long running processes)
545
692
  def tried_to_connect?(options)
546
693
  !(@connected.nil? || options[:force_reconnect])
547
694
  end
548
695
 
696
+ # We keep trying by default, but you can disable it with the
697
+ # :keep_retrying option set to false
549
698
  def should_keep_retrying?(options)
550
699
  @keep_retrying = (options[:keep_retrying].nil? || options[:keep_retrying])
551
700
  end
552
701
 
702
+ # Retry period is a minute for each failed attempt that
703
+ # we've made. This should probably do some sort of sane TCP
704
+ # backoff to prevent hammering the server, but a minute for
705
+ # each attempt seems to work reasonably well.
553
706
  def get_retry_period
554
707
  return 600 if self.connect_attempts > 6
555
708
  connect_attempts * 60
556
709
  end
557
710
 
558
- def increment_retry_period!
711
+ def increment_retry_period! #:nodoc:
559
712
  self.connect_retry_period=(get_retry_period)
560
713
  end
561
714
 
715
+ # We should only retry when there has not been a more
716
+ # serious condition that would prevent it. We increment the
717
+ # connect attempts and the retry period, to prevent constant
718
+ # connection attempts, and tell the user what we're doing by
719
+ # logging.
562
720
  def should_retry?
563
721
  if @keep_retrying
564
722
  self.connect_attempts=(connect_attempts + 1)
@@ -571,27 +729,46 @@ module NewRelic
571
729
  end
572
730
  end
573
731
 
732
+ # When we have a problem connecting to the server, we need
733
+ # to tell the user what happened, since this is not an error
734
+ # we can handle gracefully.
574
735
  def log_error(error)
575
736
  log.error "Error establishing connection with New Relic Service at #{control.server}: #{error.message}"
576
737
  log.debug error.backtrace.join("\n")
577
738
  end
578
739
 
740
+ # When the server sends us an error with the license key, we
741
+ # want to tell the user that something went wrong, and let
742
+ # them know where to go to get a valid license key
743
+ #
744
+ # After this runs, it disconnects the agent so that it will
745
+ # no longer try to connect to the server, saving the
746
+ # application and the server load
579
747
  def handle_license_error(error)
580
748
  log.error error.message
581
749
  log.info "Visit NewRelic.com to obtain a valid license key, or to upgrade your account."
582
750
  disconnect
583
751
  end
584
752
 
753
+ # If we are using a seed and token to validate the agent, we
754
+ # should debug log that fact so that debug logs include a
755
+ # clue that token authentication is what will be used
585
756
  def log_seed_token
586
757
  if control.validate_seed
587
758
  log.debug "Connecting with validation seed/token: #{control.validate_seed}/#{control.validate_token}"
588
759
  end
589
760
  end
590
761
 
762
+ # Checks whether we should send environment info, and if so,
763
+ # returns the snapshot from the local environment
591
764
  def environment_for_connect
592
765
  control['send_environment_info'] != false ? control.local_env.snapshot : []
593
766
  end
594
767
 
768
+ # These validation settings are used for cases where a
769
+ # dynamic server is spun up for clients - partners can
770
+ # include a seed and token to indicate that the host is
771
+ # allowed to connect, rather than setting a unique hostname
595
772
  def validate_settings
596
773
  {
597
774
  :seed => control.validate_seed,
@@ -599,6 +776,8 @@ module NewRelic
599
776
  }
600
777
  end
601
778
 
779
+ # Initializes the hash of settings that we send to the
780
+ # server. Returns a literal hash containing the options
602
781
  def connect_settings
603
782
  {
604
783
  :pid => $$,
@@ -611,11 +790,18 @@ module NewRelic
611
790
  :validate => validate_settings
612
791
  }
613
792
  end
793
+
794
+ # Does some simple logging to make sure that our seed and
795
+ # token for verification are correct, then returns the
796
+ # connect data passed back from the server
614
797
  def connect_to_server
615
798
  log_seed_token
616
799
  connect_data = invoke_remote(:connect, connect_settings)
617
800
  end
618
801
 
802
+ # Configures the error collector if the server says that we
803
+ # are allowed to send errors. Pretty simple, and logs at
804
+ # debug whether errors will or will not be sent.
619
805
  def configure_error_collector!(server_enabled)
620
806
  # Ask for permission to collect error data
621
807
  enabled = if error_collector.config_enabled && server_enabled
@@ -626,14 +812,24 @@ module NewRelic
626
812
  log.debug "Errors will #{enabled ? '' : 'not '}be sent to the New Relic service."
627
813
  end
628
814
 
815
+ # Random sampling is enabled based on a sample rate, which
816
+ # is the n in "every 1/n transactions is added regardless of
817
+ # its length".
818
+ #
819
+ # uses a sane default for sampling rate if the sampling rate
820
+ # is zero, since the collector currently sends '0' as a
821
+ # sampling rate for all accounts, which is probably for
822
+ # legacy reasons
629
823
  def enable_random_samples!(sample_rate)
630
- sample_rate = 10 unless sample_rate.to_i > 0# a sane default for random sampling
824
+ sample_rate = 10 unless sample_rate.to_i > 0
631
825
  @transaction_sampler.random_sampling = true
632
826
  @transaction_sampler.sampling_rate = sample_rate
633
827
  log.info "Transaction sampling enabled, rate = #{@transaction_sampler.sampling_rate}"
634
828
  end
635
829
 
636
-
830
+ # Enables or disables the transaction tracer and sets its
831
+ # options based on the options provided to the
832
+ # method.
637
833
  def configure_transaction_tracer!(server_enabled, sample_rate)
638
834
  # Ask the server for permission to send transaction samples.
639
835
  # determined by subscription license.
@@ -648,6 +844,10 @@ module NewRelic
648
844
  end
649
845
  end
650
846
 
847
+ # Asks the collector to tell us which sub-collector we
848
+ # should be reporting to, and then does the name resolution
849
+ # on that host so we don't block on DNS during the normal
850
+ # course of agent processing
651
851
  def set_collector_host!
652
852
  host = invoke_remote(:get_redirect_host)
653
853
  if host
@@ -655,11 +855,21 @@ module NewRelic
655
855
  end
656
856
  end
657
857
 
858
+ # Sets the collector host and connects to the server, then
859
+ # invokes the final configuration with the returned data
658
860
  def query_server_for_configuration
659
861
  set_collector_host!
660
862
 
661
863
  finish_setup(connect_to_server)
662
864
  end
865
+
866
+ # Takes a hash of configuration data returned from the
867
+ # server and uses it to set local variables and to
868
+ # initialize various parts of the agent that are configured
869
+ # separately.
870
+ #
871
+ # Can accommodate most arbitrary data - anything extra is
872
+ # ignored unless we say to do something with it here.
663
873
  def finish_setup(config_data)
664
874
  @agent_id = config_data['agent_run_id']
665
875
  @report_period = config_data['data_report_period']
@@ -670,7 +880,9 @@ module NewRelic
670
880
  configure_transaction_tracer!(config_data['collect_traces'], config_data['sample_rate'])
671
881
  configure_error_collector!(config_data['collect_errors'])
672
882
  end
673
-
883
+
884
+ # Logs when we connect to the server, for debugging purposes
885
+ # - makes sure we know if an agent has not connected
674
886
  def log_connection!(config_data)
675
887
  control.log! "Connected to NewRelic Service at #{@collector}"
676
888
  log.debug "Agent Run = #{@agent_id}."
@@ -679,16 +891,27 @@ module NewRelic
679
891
  end
680
892
  include Connect
681
893
 
894
+
895
+ # Serialize all the important data that the agent might want
896
+ # to send to the server. We could be sending this to file (
897
+ # common in short-running background transactions ) or
898
+ # alternately we could serialize via a pipe or socket to a
899
+ # local aggregation device
682
900
  def serialize
683
901
  accumulator = []
684
902
  accumulator[1] = harvest_transaction_traces if @transaction_sampler
685
903
  accumulator[2] = harvest_errors if @error_collector
686
904
  accumulator[0] = harvest_timeslice_data
905
+ reset_stats
906
+ @metric_ids = {}
687
907
  accumulator
688
908
  end
689
-
690
909
  public :serialize
691
910
 
911
+ # Accepts data as provided by the serialize method and merges
912
+ # it into our current collection of data to send. Can be
913
+ # dangerous if we re-merge the same data more than once - it
914
+ # will be sent multiple times.
692
915
  def merge_data_from(data)
693
916
  metrics, transaction_traces, errors = data
694
917
  @stats_engine.merge_data(metrics) if metrics
@@ -751,18 +974,26 @@ module NewRelic
751
974
  end
752
975
  end
753
976
 
977
+ # Who am I? Well, this method can tell you your hostname.
754
978
  def determine_host
755
979
  Socket.gethostname
756
980
  end
757
981
 
982
+ # Delegates to the control class to determine the root
983
+ # directory of this project
758
984
  def determine_home_directory
759
985
  control.root
760
986
  end
761
987
 
988
+ # Checks whether this process is a Passenger or Unicorn
989
+ # spawning server - if so, we probably don't intend to report
990
+ # statistics from this process
762
991
  def is_application_spawner?
763
992
  $0 =~ /ApplicationSpawner|^unicorn\S* master/
764
993
  end
765
994
 
995
+ # calls the busy harvester and collects timeslice data to
996
+ # send later
766
997
  def harvest_timeslice_data(time=Time.now)
767
998
  # this creates timeslices that are harvested below
768
999
  NewRelic::Agent::BusyCalculator.harvest_busy
@@ -772,12 +1003,18 @@ module NewRelic
772
1003
  @unsent_timeslice_data
773
1004
  end
774
1005
 
1006
+ # takes an array of arrays of spec and id, adds it into the
1007
+ # metric cache so we can save the collector some work by
1008
+ # sending integers instead of strings
775
1009
  def fill_metric_id_cache(pairs_of_specs_and_ids)
776
1010
  Array(pairs_of_specs_and_ids).each do |metric_spec, metric_id|
777
1011
  @metric_ids[metric_spec] = metric_id
778
1012
  end
779
1013
  end
780
1014
 
1015
+ # note - exceptions are logged in invoke_remote. If an exception is encountered here,
1016
+ # then the metric data is downsampled for another
1017
+ # transmission later
781
1018
  def harvest_and_send_timeslice_data
782
1019
  now = Time.now
783
1020
  NewRelic::Agent.instance.stats_engine.get_stats_no_scope('Supportability/invoke_remote').record_data_point(0.0)
@@ -786,12 +1023,13 @@ module NewRelic
786
1023
  begin
787
1024
  # In this version of the protocol, we get back an assoc array of spec to id.
788
1025
  metric_specs_and_ids = invoke_remote(:metric_data, @agent_id,
789
- @last_harvest_time.to_f,
790
- now.to_f,
791
- @unsent_timeslice_data.values)
1026
+ @last_harvest_time.to_f,
1027
+ now.to_f,
1028
+ @unsent_timeslice_data.values)
792
1029
 
793
1030
  rescue Timeout::Error
794
- # assume that the data was received. chances are that it was
1031
+ # assume that the data was received. chances are that it
1032
+ # was. Also, lol.
795
1033
  metric_specs_and_ids = []
796
1034
  end
797
1035
 
@@ -802,30 +1040,28 @@ module NewRelic
802
1040
  # if we successfully invoked this web service, then clear the unsent message cache.
803
1041
  @unsent_timeslice_data = {}
804
1042
  @last_harvest_time = now
805
-
806
- # handle_messages
807
-
808
- # note - exceptions are logged in invoke_remote. If an exception is encountered here,
809
- # then the metric data is downsampled for another timeslices
810
1043
  end
811
1044
 
1045
+ # Fills the traces array with the harvested transactions from
1046
+ # the transaction sampler, subject to the setting for slowest
1047
+ # transaction threshold
812
1048
  def harvest_transaction_traces
813
1049
  @traces = @transaction_sampler.harvest(@traces, @slowest_transaction_threshold)
814
1050
  @traces
815
1051
  end
816
1052
 
1053
+ # This handles getting the transaction traces and then sending
1054
+ # them across the wire. This includes gathering SQL
1055
+ # explanations, stripping out stack traces, and normalizing
1056
+ # SQL. note that we explain only the sql statements whose
1057
+ # segments' execution times exceed our threshold (to avoid
1058
+ # unnecessary overhead of running explains on fast queries.)
817
1059
  def harvest_and_send_slowest_sample
818
1060
  harvest_transaction_traces
819
1061
  unless @traces.empty?
820
1062
  now = Time.now
821
1063
  log.debug "Sending (#{@traces.length}) transaction traces"
822
1064
  begin
823
- # take the traces and prepare them for sending across the
824
- # wire. This includes gathering SQL explanations, stripping
825
- # out stack traces, and normalizing SQL. note that we
826
- # explain only the sql statements whose segments' execution
827
- # times exceed our threshold (to avoid unnecessary overhead
828
- # of running explains on fast queries.)
829
1065
  options = { :keep_backtraces => true }
830
1066
  options[:record_sql] = @record_sql unless @record_sql == :off
831
1067
  options[:explain_sql] = @explain_threshold if @explain_enabled
@@ -842,20 +1078,22 @@ module NewRelic
842
1078
 
843
1079
  # if we succeed sending this sample, then we don't need to keep
844
1080
  # the slowest sample around - it has been sent already and we
845
- # can collect the next one
1081
+ # can clear the collection and move on
846
1082
  @traces = nil
847
-
848
- # note - exceptions are logged in invoke_remote. If an
849
- # exception is encountered here, then the slowest sample of is
850
- # determined of the entire period since the last reported
851
- # sample.
852
1083
  end
853
1084
 
1085
+ # Gets the collection of unsent errors from the error
1086
+ # collector. We pass back in an existing array of errors that
1087
+ # may be left over from a previous send
854
1088
  def harvest_errors
855
1089
  @unsent_errors = @error_collector.harvest_errors(@unsent_errors)
856
1090
  @unsent_errors
857
1091
  end
858
1092
 
1093
+ # Handles getting the errors from the error collector and
1094
+ # sending them to the server, and any error cases like trying
1095
+ # to send very large errors - we drop the oldest error on the
1096
+ # floor and try again
859
1097
  def harvest_and_send_errors
860
1098
  harvest_errors
861
1099
  if @unsent_errors && @unsent_errors.length > 0
@@ -874,32 +1112,38 @@ module NewRelic
874
1112
  end
875
1113
  end
876
1114
 
1115
+ # This method handles the compression of the request body that
1116
+ # we are going to send to the server
1117
+ #
1118
+ # We currently optimize for CPU here since we get roughly a 10x
1119
+ # reduction in message size with this, and CPU overhead is at a
1120
+ # premium. For extra-large posts, we use the higher compression
1121
+ # since otherwise it actually errors out.
1122
+ #
1123
+ # We do not compress if content is smaller than 64kb. There are
1124
+ # problems with bugs in Ruby in some versions that expose us
1125
+ # to a risk of segfaults if we compress aggressively.
1126
+ #
1127
+ # medium payloads get fast compression, to save CPU
1128
+ # big payloads get all the compression possible, to stay under
1129
+ # the 2,000,000 byte post threshold
877
1130
  def compress_data(object)
878
1131
  dump = Marshal.dump(object)
879
1132
 
880
1133
  # this checks to make sure mongrel won't choke on big uploads
881
1134
  check_post_size(dump)
882
1135
 
883
- # we currently optimize for CPU here since we get roughly a 10x
884
- # reduction in message size with this, and CPU overhead is at a
885
- # premium. For extra-large posts, we use the higher compression
886
- # since otherwise it actually errors out.
887
-
888
1136
  dump_size = dump.size
889
1137
 
890
- # Compress if content is smaller than 64kb. There are problems
891
- # with bugs in Ruby in some versions that expose us to a risk of
892
- # segfaults if we compress aggressively.
893
1138
  return [dump, 'identity'] if dump_size < (64*1024)
894
1139
 
895
- # medium payloads get fast compression, to save CPU
896
- # big payloads get all the compression possible, to stay under
897
- # the 2,000,000 byte post threshold
898
1140
  compression = dump_size < 2000000 ? Zlib::BEST_SPEED : Zlib::BEST_COMPRESSION
899
1141
 
900
1142
  [Zlib::Deflate.deflate(dump, compression), 'deflate']
901
1143
  end
902
1144
 
1145
+ # Raises a PostTooBigException if the post_string is longer
1146
+ # than the limit configured in the control object
903
1147
  def check_post_size(post_string)
904
1148
  # TODO: define this as a config option on the server side
905
1149
  return if post_string.size < control.post_size_limit
@@ -907,6 +1151,16 @@ module NewRelic
907
1151
  raise PostTooBigException
908
1152
  end
909
1153
 
1154
+ # Posts to the specified server
1155
+ #
1156
+ # Options:
1157
+ # - :uri => the path to request on the server (a misnomer of
1158
+ # course)
1159
+ # - :encoding => the encoding to pass to the server
1160
+ # - :collector => a URI object that responds to the 'name' method
1161
+ # and returns the name of the collector to
1162
+ # contact
1163
+ # - :data => the data to send as the body of the request
910
1164
  def send_request(opts)
911
1165
  request = Net::HTTP::Post.new(opts[:uri], 'CONTENT-ENCODING' => opts[:encoding], 'HOST' => opts[:collector].name)
912
1166
  request['user-agent'] = user_agent
@@ -939,6 +1193,8 @@ module NewRelic
939
1193
  response
940
1194
  end
941
1195
 
1196
+ # Decompresses the response from the server, if it is gzip
1197
+ # encoded, otherwise returns it verbatim
942
1198
  def decompress_response(response)
943
1199
  if response['content-encoding'] != 'gzip'
944
1200
  log.debug "Uncompressed content returned"
@@ -949,6 +1205,8 @@ module NewRelic
949
1205
  i.read
950
1206
  end
951
1207
 
1208
+ # unmarshals the response and raises it if it is an exception,
1209
+ # so we can handle it in nonlocally
952
1210
  def check_for_exception(response)
953
1211
  dump = decompress_response(response)
954
1212
  value = Marshal.load(dump)
@@ -956,12 +1214,17 @@ module NewRelic
956
1214
  value
957
1215
  end
958
1216
 
1217
+ # The path on the server that we should post our data to
959
1218
  def remote_method_uri(method)
960
1219
  uri = "/agent_listener/#{PROTOCOL_VERSION}/#{control.license_key}/#{method}"
961
1220
  uri << "?run_id=#{@agent_id}" if @agent_id
962
1221
  uri
963
1222
  end
964
1223
 
1224
+ # Sets the user agent for connections to the server, to
1225
+ # conform with the HTTP spec and allow for debugging. Includes
1226
+ # the ruby version and also zlib version if available since
1227
+ # that may cause corrupt compression if there is a problem.
965
1228
  def user_agent
966
1229
  ruby_description = ''
967
1230
  # note the trailing space!
@@ -971,7 +1234,10 @@ module NewRelic
971
1234
  "NewRelic-RubyAgent/#{NewRelic::VERSION::STRING} #{ruby_description}#{zlib_version}"
972
1235
  end
973
1236
 
974
- # send a message via post
1237
+ # send a message via post to the actual server. This attempts
1238
+ # to automatically compress the data via zlib if it is large
1239
+ # enough to be worth compressing, and handles any errors the
1240
+ # server may return
975
1241
  def invoke_remote(method, *args)
976
1242
  now = Time.now
977
1243
  #determines whether to zip the data or send plain
@@ -992,20 +1258,31 @@ module NewRelic
992
1258
  NewRelic::Agent.instance.stats_engine.get_stats_no_scope('Supportability/invoke_remote/' + method.to_s).record_data_point((Time.now - now).to_f)
993
1259
  end
994
1260
 
1261
+ def save_or_transmit_data
1262
+ if NewRelic::DataSerialization.should_send_data?
1263
+ log.debug "Sending data to New Relic Service"
1264
+ NewRelic::Agent.load_data
1265
+ harvest_and_send_errors
1266
+ harvest_and_send_slowest_sample
1267
+ harvest_and_send_timeslice_data
1268
+ else
1269
+ log.debug "Serializing agent data to disk"
1270
+ NewRelic::Agent.save_data
1271
+ end
1272
+ end
1273
+
1274
+ # This method contacts the server to send remaining data and
1275
+ # let the server know that the agent is shutting down - this
1276
+ # allows us to do things like accurately set the end of the
1277
+ # lifetime of the process
1278
+ #
1279
+ # If this process comes from a parent process, it will not
1280
+ # disconnect, so that the parent process can continue to send data
995
1281
  def graceful_disconnect
996
1282
  if @connected
997
1283
  begin
998
1284
  @request_timeout = 10
999
- if NewRelic::DataSerialization.should_send_data?
1000
- log.debug "Sending data to New Relic Service"
1001
- NewRelic::Agent.load_data
1002
- harvest_and_send_errors
1003
- harvest_and_send_slowest_sample
1004
- harvest_and_send_timeslice_data
1005
- else
1006
- log.debug "Serializing agent data to disk"
1007
- NewRelic::Agent.save_data
1008
- end
1285
+ save_or_transmit_data
1009
1286
  if @connected_pid == $$
1010
1287
  log.debug "Sending New Relic service agent run shutdown message"
1011
1288
  invoke_remote :shutdown, @agent_id, Time.now.to_f