newrelic_rpm 2.13.4 → 2.13.5.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 (57) hide show
  1. data/CHANGELOG +9 -0
  2. data/lib/new_relic/agent.rb +2 -1
  3. data/lib/new_relic/agent/agent.rb +393 -204
  4. data/lib/new_relic/agent/error_collector.rb +113 -43
  5. data/lib/new_relic/agent/instrumentation/controller_instrumentation.rb +14 -16
  6. data/lib/new_relic/agent/instrumentation/queue_time.rb +201 -0
  7. data/lib/new_relic/agent/instrumentation/rails3/action_controller.rb +1 -1
  8. data/lib/new_relic/agent/instrumentation/sequel.rb +95 -0
  9. data/lib/new_relic/agent/method_tracer.rb +391 -313
  10. data/lib/new_relic/agent/samplers/cpu_sampler.rb +43 -41
  11. data/lib/new_relic/agent/samplers/delayed_job_lock_sampler.rb +2 -0
  12. data/lib/new_relic/agent/samplers/memory_sampler.rb +122 -120
  13. data/lib/new_relic/agent/samplers/object_sampler.rb +2 -0
  14. data/lib/new_relic/agent/stats_engine/metric_stats.rb +0 -1
  15. data/lib/new_relic/agent/stats_engine/samplers.rb +20 -14
  16. data/lib/new_relic/agent/stats_engine/transactions.rb +35 -7
  17. data/lib/new_relic/control.rb +12 -17
  18. data/lib/new_relic/control/configuration.rb +1 -0
  19. data/lib/new_relic/control/frameworks/rails.rb +7 -4
  20. data/lib/new_relic/control/frameworks/rails3.rb +1 -1
  21. data/lib/new_relic/control/instrumentation.rb +2 -18
  22. data/lib/new_relic/local_environment.rb +117 -59
  23. data/lib/new_relic/rack/developer_mode.rb +212 -207
  24. data/lib/new_relic/recipes.rb +0 -9
  25. data/lib/new_relic/stats.rb +87 -81
  26. data/lib/new_relic/transaction_analysis.rb +1 -1
  27. data/lib/new_relic/version.rb +2 -2
  28. data/lib/newrelic_rpm.rb +2 -3
  29. data/lib/tasks/tests.rake +5 -1
  30. data/newrelic_rpm.gemspec +14 -5
  31. data/test/config/test_control.rb +14 -2
  32. data/test/new_relic/agent/active_record_instrumentation_test.rb +342 -119
  33. data/test/new_relic/agent/add_method_tracer_test.rb +158 -0
  34. data/test/new_relic/agent/agent_connect_test.rb +295 -0
  35. data/test/new_relic/agent/agent_controller_test.rb +86 -18
  36. data/test/new_relic/agent/agent_start_test.rb +326 -0
  37. data/test/new_relic/agent/agent_start_worker_thread_test.rb +157 -0
  38. data/test/new_relic/agent/apdex_from_server_test.rb +9 -0
  39. data/test/new_relic/agent/collection_helper_test.rb +3 -1
  40. data/test/new_relic/agent/error_collector_notice_error_test.rb +255 -0
  41. data/test/new_relic/agent/error_collector_test.rb +6 -0
  42. data/test/new_relic/agent/method_tracer_test.rb +2 -2
  43. data/test/new_relic/agent/method_tracer_trace_execution_scoped_test.rb +233 -0
  44. data/test/new_relic/agent/net_instrumentation_test.rb +17 -12
  45. data/test/new_relic/agent/queue_time_test.rb +333 -0
  46. data/test/new_relic/agent/rpm_agent_test.rb +4 -2
  47. data/test/new_relic/agent/stats_engine/samplers_test.rb +27 -1
  48. data/test/new_relic/agent/transaction_sample_subtest_test.rb +56 -0
  49. data/test/new_relic/agent/transaction_sample_test.rb +103 -174
  50. data/test/new_relic/agent/transaction_sampler_test.rb +9 -2
  51. data/test/new_relic/control_test.rb +7 -2
  52. data/test/new_relic/metric_spec_test.rb +1 -1
  53. data/test/new_relic/stats_test.rb +112 -15
  54. data/test/test_helper.rb +79 -16
  55. data/ui/helpers/developer_mode_helper.rb +2 -0
  56. metadata +19 -7
  57. data/lib/new_relic_api.rb +0 -276
data/CHANGELOG CHANGED
@@ -1,3 +1,12 @@
1
+ v2.13.5
2
+ * Moved the API helper to the github newrelic_api gem.
3
+ * Revamped queue time to include server, queue, and middleware time
4
+ * Increased test coverage and stability
5
+ * Add Trinidad as a dispatcher (from Calavera, on github)
6
+ * Sequel instrumentation from Aman Gupta
7
+ * patches to 1.9 compatibility from dkastner on github
8
+ * Support for 1.9.2's garbage collection instrumentation from Justin Weiss
9
+
1
10
  v2.13.4
2
11
  * Update DNS lookup code to remove hardcoded IP addresses
3
12
 
@@ -70,7 +70,8 @@ module NewRelic
70
70
  require 'new_relic/noticed_error'
71
71
  require 'new_relic/histogram'
72
72
  require 'new_relic/timer_lib'
73
-
73
+
74
+ require 'new_relic/agent'
74
75
  require 'new_relic/agent/chained_call'
75
76
  require 'new_relic/agent/agent'
76
77
  require 'new_relic/agent/shim_agent'
@@ -37,6 +37,7 @@ module NewRelic
37
37
  @transaction_sampler = NewRelic::Agent::TransactionSampler.new
38
38
  @stats_engine.transaction_sampler = @transaction_sampler
39
39
  @error_collector = NewRelic::Agent::ErrorCollector.new
40
+ @connect_attempts = 0
40
41
 
41
42
  @request_timeout = NewRelic::Control.instance.fetch('timeout', 2 * 60)
42
43
 
@@ -94,11 +95,6 @@ module NewRelic
94
95
  # busy time ?
95
96
  end
96
97
 
97
- # This method is deprecated. Use NewRelic::Agent.manual_start
98
- def manual_start(ignored=nil, also_ignored=nil)
99
- raise "This method no longer supported. Instead use the class method NewRelic::Agent.manual_start"
100
- end
101
-
102
98
  # This method should be called in a forked process after a fork.
103
99
  # It assumes the parent process initialized the agent, but does
104
100
  # not assume the agent started.
@@ -226,80 +222,158 @@ module NewRelic
226
222
  def log
227
223
  NewRelic::Agent.logger
228
224
  end
225
+
226
+ # Herein lies the corpse of the former 'start' method. May
227
+ # it's unmatched flog score rest in pieces.
228
+ module Start
229
+ def already_started?
230
+ if started?
231
+ control.log!("Agent Started Already!", :error)
232
+ true
233
+ end
234
+ end
229
235
 
230
- # Start up the agent. This verifies that the agent_enabled? is
231
- # true and initializes the sampler based on the current
232
- # configuration settings. Then it will fire up the background
233
- # thread for sending data to the server if applicable.
234
- def start
235
- if started?
236
- control.log! "Agent Started Already!", :error
237
- return
236
+ def disabled?
237
+ !control.agent_enabled?
238
+ end
239
+
240
+ def log_dispatcher
241
+ dispatcher_name = control.dispatcher.to_s
242
+ return if log_if(dispatcher_name.empty?, :info, "No dispatcher detected.")
243
+ log.info "Dispatcher: #{dispatcher_name}"
244
+ end
245
+
246
+ def log_app_names
247
+ log.info "Application: #{control.app_names.join(", ")}"
238
248
  end
239
- return if !control.agent_enabled?
240
- @started = true
241
- @local_host = determine_host
242
249
 
243
- if control.dispatcher.nil? || control.dispatcher.to_s.empty?
244
- log.info "No dispatcher detected."
245
- else
246
- log.info "Dispatcher: #{control.dispatcher.to_s}"
247
- end
248
- log.info "Application: #{control.app_names.join(", ")}" unless control.app_names.empty?
249
-
250
- sampler_config = control.fetch('transaction_tracer', {})
251
- # TODO: Should move this state into the transaction sampler instance
252
- @should_send_samples = @config_should_send_samples = sampler_config.fetch('enabled', true)
253
- @should_send_random_samples = sampler_config.fetch('random_sample', false)
254
- @explain_threshold = sampler_config.fetch('explain_threshold', 0.5).to_f
255
- @explain_enabled = sampler_config.fetch('explain_enabled', true)
256
- @record_sql = sampler_config.fetch('record_sql', :obfuscated).to_sym
257
-
258
- # use transaction_threshold: 4.0 to force the TT collection
259
- # threshold to 4 seconds
260
- # use transaction_threshold: apdex_f to use your apdex t value
261
- # multiplied by 4
262
- # undefined transaction_threshold defaults to 2.0
263
- apdex_f = 4 * NewRelic::Control.instance.apdex_t
264
- @slowest_transaction_threshold = sampler_config.fetch('transaction_threshold', 2.0)
265
- if @slowest_transaction_threshold =~ /apdex_f/i
266
- @slowest_transaction_threshold = apdex_f
267
- end
268
- @slowest_transaction_threshold = @slowest_transaction_threshold.to_f
269
-
270
- log.warn "Agent is configured to send raw SQL to RPM service" if @record_sql == :raw
271
-
272
- case
273
- when !control.monitor_mode?
274
- log.warn "Agent configured not to send data in this environment - edit newrelic.yml to change this"
275
- when !control.license_key
276
- log.error "No license key found. Please edit your newrelic.yml file and insert your license key."
277
- when control.license_key.length != 40
278
- log.error "Invalid license key: #{control.license_key}"
279
- when [:passenger, :unicorn].include?(control.dispatcher)
280
- log.info "Connecting workers after forking."
281
- else
282
- # Do the connect in the foreground if we are in sync mode
283
- NewRelic::Agent.disable_all_tracing { connect(:keep_retrying => false) } if control.sync_startup
250
+ def apdex_f
251
+ (4 * NewRelic::Control.instance.apdex_t).to_f
252
+ end
284
253
 
285
- # Start the event loop and initiate connection if necessary
286
- start_worker_thread
254
+ def apdex_f_threshold?
255
+ sampler_config.fetch('transaction_threshold', '') =~ /apdex_f/i
256
+ end
287
257
 
288
- # Our shutdown handler needs to run after other shutdown handlers
289
- # that may be doing things like running the app (hello sinatra).
290
- if control.send_data_on_exit
291
- if RUBY_VERSION =~ /rubinius/i
292
- list = at_exit { shutdown }
293
- # move the shutdown handler to the front of the list, to
294
- # execute last:
295
- list.unshift(list.pop)
296
- elsif !defined?(JRuby) or !defined?(Sinatra::Application)
297
- at_exit { at_exit { shutdown } }
298
- end
258
+ def set_sql_recording!
259
+ @record_sql = sampler_config.fetch('record_sql', :obfuscated).to_sym
260
+ log_sql_transmission_warning?
261
+ end
262
+
263
+ def log_sql_transmission_warning?
264
+ log_if((@record_sql == :raw), :warn, "Agent is configured to send raw SQL to RPM service")
265
+ end
266
+
267
+ def sampler_config
268
+ control.fetch('transaction_tracer', {})
269
+ end
270
+
271
+ # this entire method should be done on the transaction
272
+ # sampler object, rather than here. We should pass in the
273
+ # sampler config.
274
+ def config_transaction_tracer
275
+ @should_send_samples = @config_should_send_samples = sampler_config.fetch('enabled', true)
276
+ @should_send_random_samples = sampler_config.fetch('random_sample', false)
277
+ @explain_threshold = sampler_config.fetch('explain_threshold', 0.5).to_f
278
+ @explain_enabled = sampler_config.fetch('explain_enabled', true)
279
+ set_sql_recording!
280
+
281
+ # default to 2.0, string 'apdex_f' will turn into your
282
+ # apdex * 4
283
+ @slowest_transaction_threshold = sampler_config.fetch('transaction_threshold', 2.0).to_f
284
+ @slowest_transaction_threshold = apdex_f if apdex_f_threshold?
285
+ end
286
+
287
+ def connect_in_foreground
288
+ NewRelic::Agent.disable_all_tracing { connect(:keep_retrying => false) }
289
+ end
290
+
291
+ def using_rubinius?
292
+ RUBY_VERSION =~ /rubinius/i
293
+ end
294
+
295
+ def using_jruby?
296
+ defined?(JRuby)
297
+ end
298
+
299
+ def using_sinatra?
300
+ defined?(Sinatra::Application)
301
+ end
302
+
303
+ # we should not set an at_exit block if people are using
304
+ # these as they don't do standard at_exit behavior per MRI/YARV
305
+ def weird_ruby?
306
+ using_rubinius? || using_jruby? || using_sinatra?
307
+ end
308
+
309
+ def install_exit_handler
310
+ if control.send_data_on_exit && !weird_ruby?
311
+ # Our shutdown handler needs to run after other shutdown handlers
312
+ at_exit { at_exit { shutdown } }
299
313
  end
300
314
  end
301
- log.info "New Relic RPM Agent #{NewRelic::VERSION::STRING} Initialized: pid = #$$"
302
- log.info "Agent Log found in #{NewRelic::Control.instance.log_file}" if NewRelic::Control.instance.log_file
315
+
316
+ def notify_log_file_location
317
+ log_file = NewRelic::Control.instance.log_file
318
+ log_if(log_file, :info, "Agent Log found in #{log_file}")
319
+ end
320
+
321
+ def log_version_and_pid
322
+ log.info "New Relic RPM Agent #{NewRelic::VERSION::STRING} Initialized: pid = #{$$}"
323
+ end
324
+
325
+ def log_if(boolean, level, message)
326
+ self.log.send(level, message) if boolean
327
+ boolean
328
+ end
329
+
330
+ def log_unless(boolean, level, message)
331
+ self.log.send(level, message) unless boolean
332
+ boolean
333
+ end
334
+
335
+ def monitoring?
336
+ log_unless(control.monitor_mode?, :warn, "Agent configured not to send data in this environment - edit newrelic.yml to change this")
337
+ end
338
+
339
+ def has_license_key?
340
+ log_unless(control.license_key, :error, "No license key found. Please edit your newrelic.yml file and insert your license key.")
341
+ end
342
+
343
+ def has_correct_license_key?
344
+ has_license_key? && correct_license_length
345
+ end
346
+
347
+ def correct_license_length
348
+ key = control.license_key
349
+ log_unless((key.length == 40), :error, "Invalid license key: #{key}")
350
+ end
351
+
352
+ def using_forking_dispatcher?
353
+ log_if([:passenger, :unicorn].include?(control.dispatcher), :info, "Connecting workers after forking.")
354
+ end
355
+
356
+ def check_config_and_start_agent
357
+ return unless monitoring? && has_correct_license_key?
358
+ return if using_forking_dispatcher?
359
+ connect_in_foreground if control.sync_startup
360
+ start_worker_thread
361
+ install_exit_handler
362
+ end
363
+ end
364
+
365
+ include Start
366
+
367
+ def start
368
+ return if already_started? || disabled?
369
+ @started = true
370
+ @local_host = determine_host
371
+ log_dispatcher
372
+ log_app_names
373
+ config_transaction_tracer
374
+ check_config_and_start_agent
375
+ log_version_and_pid
376
+ notify_log_file_location
303
377
  end
304
378
 
305
379
  # Clear out the metric data, errors, and transaction traces. Reset the histogram data.
@@ -318,13 +392,77 @@ module NewRelic
318
392
  @collector ||= control.server
319
393
  end
320
394
 
321
- # Try to launch the worker thread and connect to the server.
322
- #
323
- # See #connect for a description of connection_options.
324
- def start_worker_thread(connection_options = {})
325
- log.debug "Creating RPM worker thread."
326
- @worker_thread = Thread.new do
327
- begin
395
+ module StartWorkerThread
396
+
397
+ def check_transaction_sampler_status
398
+ # disable transaction sampling if disabled by the server
399
+ # and we're not in dev mode
400
+ if control.developer_mode? || @should_send_samples
401
+ @transaction_sampler.enable
402
+ else
403
+ @transaction_sampler.disable
404
+ end
405
+ end
406
+
407
+ def log_worker_loop_start
408
+ log.info "Reporting performance data every #{@report_period} seconds."
409
+ log.debug "Running worker loop"
410
+ end
411
+
412
+ def create_and_run_worker_loop
413
+ @worker_loop = WorkerLoop.new
414
+ @worker_loop.run(@report_period) do
415
+ harvest_and_send_timeslice_data
416
+ harvest_and_send_slowest_sample if @should_send_samples
417
+ harvest_and_send_errors if error_collector.enabled
418
+ end
419
+ end
420
+
421
+ def handle_force_restart(error)
422
+ log.info error.message
423
+ # disconnect and start over.
424
+ # clear the stats engine
425
+ reset_stats
426
+ @metric_ids = {}
427
+ @connected = nil
428
+ # Wait a short time before trying to reconnect
429
+ sleep 30
430
+ end
431
+
432
+ def handle_force_disconnect(error)
433
+ # when a disconnect is requested, stop the current thread, which
434
+ # is the worker thread that gathers data and talks to the
435
+ # server.
436
+ log.error "RPM forced this agent to disconnect (#{error.message})"
437
+ disconnect
438
+ end
439
+
440
+ def handle_server_connection_problem(error)
441
+ log.error "Unable to establish connection with the server. Run with log level set to debug for more information."
442
+ log.debug("#{error.class.name}: #{error.message}\n#{error.backtrace.first}")
443
+ disconnect
444
+ end
445
+
446
+ def handle_other_error(error)
447
+ log.error "Terminating worker loop: #{error.class.name}: #{error.message}\n #{error.backtrace.join("\n ")}"
448
+ disconnect
449
+ end
450
+
451
+ def catch_errors
452
+ yield
453
+ rescue NewRelic::Agent::ForceRestartException => e
454
+ handle_force_restart(e)
455
+ retry
456
+ rescue NewRelic::Agent::ForceDisconnectException => e
457
+ handle_force_disconnect(e)
458
+ rescue NewRelic::Agent::ServerConnectionException => e
459
+ handle_server_connection_problem(e)
460
+ rescue Exception => e
461
+ handle_other_error(e)
462
+ end
463
+
464
+ def deferred_work!(connection_options)
465
+ catch_errors do
328
466
  NewRelic::Agent.disable_all_tracing do
329
467
  # We try to connect. If this returns false that means
330
468
  # the server rejected us for a licensing reason and we should
@@ -332,51 +470,25 @@ module NewRelic
332
470
  # that means it didn't try to connect because we're in the master.
333
471
  connect(connection_options)
334
472
  if @connected
335
- # disable transaction sampling if disabled by the server and we're not in dev mode
336
- if !control.developer_mode? && !@should_send_samples
337
- @transaction_sampler.disable
338
- else
339
- @transaction_sampler.enable # otherwise ensure TT's are enabled
340
- end
341
-
342
- log.info "Reporting performance data every #{@report_period} seconds."
343
- log.debug "Running worker loop"
344
- # Note if the agent attempts to report more frequently than allowed by the server
345
- # the server will start dropping data.
346
- @worker_loop = WorkerLoop.new
347
- @worker_loop.run(@report_period) do
348
- harvest_and_send_timeslice_data
349
- harvest_and_send_slowest_sample if @should_send_samples
350
- harvest_and_send_errors if error_collector.enabled
351
- end
473
+ check_transaction_sampler_status
474
+ log_worker_loop_start
475
+ create_and_run_worker_loop
352
476
  else
353
- log.debug "No connection. Worker thread finished."
477
+ log.debug "No connection. Worker thread ending."
354
478
  end
355
479
  end
356
- rescue NewRelic::Agent::ForceRestartException => e
357
- log.info e.message
358
- # disconnect and start over.
359
- # clear the stats engine
360
- reset_stats
361
- @metric_ids = {}
362
- @connected = nil
363
- # Wait a short time before trying to reconnect
364
- sleep 30
365
- retry
366
- rescue NewRelic::Agent::ForceDisconnectException => e
367
- # when a disconnect is requested, stop the current thread, which
368
- # is the worker thread that gathers data and talks to the
369
- # server.
370
- log.error "RPM forced this agent to disconnect (#{e.message})"
371
- @connected = false
372
- rescue NewRelic::Agent::ServerConnectionException => e
373
- log.error "Unable to establish connection with the server. Run with log level set to debug for more information."
374
- log.debug("#{e.class.name}: #{e.message}\n#{e.backtrace.first}")
375
- @connected = false
376
- rescue Exception => e
377
- log.error "Terminating worker loop: #{e.class.name}: #{e}\n #{e.backtrace.join("\n ")}"
378
- @connected = false
379
- end # begin
480
+ end
481
+ end
482
+ end
483
+ include StartWorkerThread
484
+
485
+ # Try to launch the worker thread and connect to the server.
486
+ #
487
+ # See #connect for a description of connection_options.
488
+ def start_worker_thread(connection_options = {})
489
+ log.debug "Creating RPM worker thread."
490
+ @worker_thread = Thread.new do
491
+ deferred_work!(connection_options)
380
492
  end # thread new
381
493
  @worker_thread['newrelic_label'] = 'Worker Loop'
382
494
  end
@@ -384,112 +496,189 @@ module NewRelic
384
496
  def control
385
497
  NewRelic::Control.instance
386
498
  end
499
+
500
+ module Connect
501
+ attr_accessor :connect_retry_period
502
+ attr_accessor :connect_attempts
387
503
 
388
- # Connect to the server and validate the license. If successful,
389
- # @connected has true when finished. If not successful, you can
390
- # keep calling this. Return false if we could not establish a
391
- # connection with the server and we should not retry, such as if
392
- # there's a bad license key.
393
- #
394
- # Set keep_retrying=false to disable retrying and return asap, such as when
395
- # invoked in the foreground. Otherwise this runs until a successful
396
- # connection is made, or the server rejects us.
397
- #
398
- # * <tt>:keep_retrying => false</tt> to only try to connect once, and
399
- # return with the connection set to nil. This ensures we may try again
400
- # later (default true).
401
- # * <tt>force_reconnect => true</tt> if you want to establish a new connection
402
- # to the server before running the worker loop. This means you get a separate
403
- # agent run and RPM sees it as a separate instance (default is false).
404
- def connect(options)
405
- # Don't proceed if we already connected (@connected=true) or if we tried
406
- # to connect and were rejected with prejudice because of a license issue
407
- # (@connected=false).
408
- return if !@connected.nil? && !options[:force_reconnect]
409
- keep_retrying = options[:keep_retrying].nil? || options[:keep_retrying]
504
+ def disconnect
505
+ @connected = false
506
+ true
507
+ end
410
508
 
411
- # wait a few seconds for the web server to boot, necessary in development
412
- connect_retry_period = keep_retrying ? 10 : 0
413
- connect_attempts = 0
414
- @agent_id = nil
415
- begin
416
- sleep connect_retry_period.to_i
417
- log.debug "Connecting Process to RPM: #$0"
418
- host = invoke_remote(:get_redirect_host)
419
- @collector = control.server_from_host(host) if host
420
- environment = control['send_environment_info'] != false ? control.local_env.snapshot : []
421
- log.debug "Connecting with validation seed/token: #{control.validate_seed}/#{control.validate_token}" if control.validate_seed
422
- connect_data = invoke_remote :connect,
509
+ def tried_to_connect?(options)
510
+ !(@connected.nil? || options[:force_reconnect])
511
+ end
512
+
513
+ def should_keep_retrying?(options)
514
+ @keep_retrying = (options[:keep_retrying].nil? || options[:keep_retrying])
515
+ end
516
+
517
+ def get_retry_period
518
+ return 600 if self.connect_attempts > 6
519
+ connect_attempts * 60
520
+ end
521
+
522
+ def increment_retry_period!
523
+ self.connect_retry_period=(get_retry_period)
524
+ end
525
+
526
+ def should_retry?
527
+ if @keep_retrying
528
+ self.connect_attempts=(connect_attempts + 1)
529
+ increment_retry_period!
530
+ log.info "Will re-attempt in #{connect_retry_period} seconds"
531
+ true
532
+ else
533
+ disconnect
534
+ false
535
+ end
536
+ end
537
+
538
+ def log_error(error)
539
+ log.error "Error establishing connection with New Relic RPM Service at #{control.server}: #{error.message}"
540
+ log.debug error.backtrace.join("\n")
541
+ end
542
+
543
+ def handle_license_error(error)
544
+ log.error error.message
545
+ log.info "Visit NewRelic.com to obtain a valid license key, or to upgrade your account."
546
+ disconnect
547
+ end
548
+
549
+ def log_seed_token
550
+ if control.validate_seed
551
+ log.debug "Connecting with validation seed/token: #{control.validate_seed}/#{control.validate_token}"
552
+ end
553
+ end
554
+
555
+ def environment_for_connect
556
+ control['send_environment_info'] != false ? control.local_env.snapshot : []
557
+ end
558
+
559
+ def validate_settings
560
+ {
561
+ :seed => control.validate_seed,
562
+ :token => control.validate_token
563
+ }
564
+ end
565
+
566
+ def connect_settings
567
+ {
423
568
  :pid => $$,
424
569
  :host => @local_host,
425
570
  :app_name => control.app_names,
426
571
  :language => 'ruby',
427
572
  :agent_version => NewRelic::VERSION::STRING,
428
- :environment => environment,
573
+ :environment => environment_for_connect,
429
574
  :settings => control.settings,
430
- :validate => {:seed => control.validate_seed,
431
- :token => control.validate_token }
432
-
433
- @agent_id = connect_data['agent_run_id']
434
- @report_period = connect_data['data_report_period']
435
- @url_rules = connect_data['url_rules']
575
+ :validate => validate_settings
576
+ }
577
+ end
578
+ def connect_to_server
579
+ log_seed_token
580
+ connect_data = invoke_remote(:connect, connect_settings)
581
+ end
436
582
 
437
- control.log! "Connected to NewRelic Service at #{@collector}"
438
- log.debug "Agent Run = #{@agent_id}."
439
- log.debug "Connection data = #{connect_data.inspect}"
583
+ def configure_error_collector!(server_enabled)
584
+ # Ask for permission to collect error data
585
+ enabled = if error_collector.config_enabled && server_enabled
586
+ error_collector.enabled = true
587
+ else
588
+ error_collector.enabled = false
589
+ end
590
+ log.debug "Errors will #{enabled ? '' : 'not '}be sent to the RPM service."
591
+ end
592
+
593
+ def enable_random_samples!(sample_rate)
594
+ @transaction_sampler.random_sampling = true
595
+ @transaction_sampler.sampling_rate = sample_rate
596
+ log.info "Transaction sampling enabled, rate = #{@transaction_sampler.sampling_rate}"
597
+ end
598
+
440
599
 
600
+ def configure_transaction_tracer!(server_enabled, sample_rate)
441
601
  # Ask the server for permission to send transaction samples.
442
602
  # determined by subscription license.
443
- @should_send_samples = @config_should_send_samples && connect_data['collect_traces']
603
+ @should_send_samples = @config_should_send_samples && server_enabled
444
604
 
445
605
  if @should_send_samples
446
- if @should_send_random_samples
447
- @transaction_sampler.random_sampling = true
448
- @transaction_sampler.sampling_rate = connect_data['sampling_rate']
449
- log.info "Transaction sampling enabled, rate = #{@transaction_sampler.sampling_rate}"
450
- end
606
+ # I don't think this is ever true, but...
607
+ enable_random_samples!(sample_rate) if @should_send_random_samples
451
608
  log.debug "Transaction tracing threshold is #{@slowest_transaction_threshold} seconds."
452
609
  else
453
610
  log.debug "Transaction traces will not be sent to the RPM service."
454
611
  end
612
+ end
455
613
 
456
- # Ask for permission to collect error data
457
- error_collector.enabled = error_collector.config_enabled && connect_data['collect_errors']
614
+ def set_collector_host!
615
+ host = invoke_remote(:get_redirect_host)
616
+ if host
617
+ @collector = control.server_from_host(host)
618
+ end
619
+ end
458
620
 
459
- log.debug "Errors will be sent to the RPM service." if error_collector.enabled
621
+ def query_server_for_configuration
622
+ set_collector_host!
623
+
624
+ finish_setup(connect_to_server)
625
+ end
626
+ def finish_setup(config_data)
627
+ @agent_id = config_data['agent_run_id']
628
+ @report_period = config_data['data_report_period']
629
+ @url_rules = config_data['url_rules']
630
+
631
+ log_connection!(config_data)
632
+ configure_transaction_tracer!(config_data['collect_traces'], config_data['sample_rate'])
633
+ configure_error_collector!(config_data['collect_errors'])
634
+ end
460
635
 
461
- @connected_pid = $$
462
- @connected = true
636
+ def log_connection!(config_data)
637
+ control.log! "Connected to NewRelic Service at #{@collector}"
638
+ log.debug "Agent Run = #{@agent_id}."
639
+ log.debug "Connection data = #{config_data.inspect}"
640
+ end
641
+ end
642
+ include Connect
463
643
 
464
- rescue NewRelic::Agent::LicenseException => e
465
- log.error e.message
466
- log.info "Visit NewRelic.com to obtain a valid license key, or to upgrade your account."
467
- @connected = false
644
+ # Connect to the server and validate the license. If successful,
645
+ # @connected has true when finished. If not successful, you can
646
+ # keep calling this. Return false if we could not establish a
647
+ # connection with the server and we should not retry, such as if
648
+ # there's a bad license key.
649
+ #
650
+ # Set keep_retrying=false to disable retrying and return asap, such as when
651
+ # invoked in the foreground. Otherwise this runs until a successful
652
+ # connection is made, or the server rejects us.
653
+ #
654
+ # * <tt>:keep_retrying => false</tt> to only try to connect once, and
655
+ # return with the connection set to nil. This ensures we may try again
656
+ # later (default true).
657
+ # * <tt>force_reconnect => true</tt> if you want to establish a new connection
658
+ # to the server before running the worker loop. This means you get a separate
659
+ # agent run and RPM sees it as a separate instance (default is false).
660
+ def connect(options)
661
+ # Don't proceed if we already connected (@connected=true) or if we tried
662
+ # to connect and were rejected with prejudice because of a license issue
663
+ # (@connected=false), unless we're forced to by force_reconnect.
664
+ return if tried_to_connect?(options)
468
665
 
469
- rescue Timeout::Error, StandardError => e
470
- if e.instance_of? NewRelic::Agent::ServerConnectionException
471
- log.info "Unable to establish connection with New Relic RPM Service at #{control.server}: #{e.message}"
472
- log.debug e.backtrace.join("\n")
473
- else
474
- log.error "Error establishing connection with New Relic RPM Service at #{control.server}: #{e.message}"
475
- log.debug e.backtrace.join("\n")
476
- end
477
- # retry logic
478
- if keep_retrying
479
- connect_attempts += 1
480
- case connect_attempts
481
- when 1..2
482
- connect_retry_period, period_msg = 60, "1 minute"
483
- when 3..5
484
- connect_retry_period, period_msg = 60 * 2, "2 minutes"
485
- else
486
- connect_retry_period, period_msg = 5 * 60, "5 minutes"
487
- end
488
- log.info "Will re-attempt in #{period_msg}"
489
- retry
490
- else
491
- @connected = nil
492
- end
666
+ # wait a few seconds for the web server to boot, necessary in development
667
+ @connect_retry_period = should_keep_retrying?(options) ? 10 : 0
668
+
669
+ sleep connect_retry_period
670
+ log.debug "Connecting Process to RPM: #$0"
671
+ query_server_for_configuration
672
+ @connected_pid = $$
673
+ @connected = true
674
+ rescue NewRelic::Agent::LicenseException => e
675
+ handle_license_error(e)
676
+ rescue Timeout::Error, StandardError => e
677
+ log_error(e)
678
+ if should_retry?
679
+ retry
680
+ else
681
+ disconnect
493
682
  end
494
683
  end
495
684