newrelic_rpm 2.12.3 → 2.13.0.beta3

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 (118) hide show
  1. data/CHANGELOG +24 -2
  2. data/README.rdoc +172 -0
  3. data/bin/newrelic +13 -0
  4. data/bin/newrelic_cmd +2 -1
  5. data/install.rb +8 -45
  6. data/lib/new_relic/agent.rb +43 -30
  7. data/lib/new_relic/agent/agent.rb +699 -631
  8. data/lib/new_relic/agent/busy_calculator.rb +81 -81
  9. data/lib/new_relic/agent/error_collector.rb +9 -6
  10. data/lib/new_relic/agent/instrumentation/active_record_instrumentation.rb +2 -2
  11. data/lib/new_relic/agent/instrumentation/controller_instrumentation.rb +10 -5
  12. data/lib/new_relic/agent/instrumentation/data_mapper.rb +13 -45
  13. data/lib/new_relic/agent/instrumentation/memcache.rb +15 -4
  14. data/lib/new_relic/agent/instrumentation/metric_frame.rb +37 -29
  15. data/lib/new_relic/agent/instrumentation/rack.rb +20 -34
  16. data/lib/new_relic/agent/method_tracer.rb +9 -9
  17. data/lib/new_relic/agent/samplers/delayed_job_lock_sampler.rb +31 -31
  18. data/lib/new_relic/agent/stats_engine/metric_stats.rb +5 -5
  19. data/lib/new_relic/agent/stats_engine/samplers.rb +3 -0
  20. data/lib/new_relic/agent/transaction_sampler.rb +31 -15
  21. data/lib/new_relic/agent/worker_loop.rb +29 -28
  22. data/lib/new_relic/collection_helper.rb +4 -2
  23. data/lib/new_relic/command.rb +85 -0
  24. data/lib/new_relic/commands/deployments.rb +74 -114
  25. data/lib/new_relic/commands/install.rb +81 -0
  26. data/lib/new_relic/control.rb +54 -379
  27. data/lib/new_relic/control/configuration.rb +149 -0
  28. data/lib/new_relic/control/{external.rb → frameworks/external.rb} +2 -2
  29. data/lib/new_relic/control/{merb.rb → frameworks/merb.rb} +1 -1
  30. data/lib/new_relic/control/frameworks/rails.rb +126 -0
  31. data/lib/new_relic/control/{rails3.rb → frameworks/rails3.rb} +11 -10
  32. data/lib/new_relic/control/{ruby.rb → frameworks/ruby.rb} +1 -1
  33. data/lib/new_relic/control/{sinatra.rb → frameworks/sinatra.rb} +2 -2
  34. data/lib/new_relic/control/instrumentation.rb +84 -0
  35. data/lib/new_relic/control/logging_methods.rb +74 -0
  36. data/lib/new_relic/control/profiling.rb +24 -0
  37. data/lib/new_relic/control/server_methods.rb +88 -0
  38. data/lib/new_relic/local_environment.rb +3 -3
  39. data/lib/new_relic/metric_parser.rb +13 -2
  40. data/lib/new_relic/metric_parser/active_record.rb +4 -1
  41. data/lib/new_relic/metric_parser/apdex.rb +53 -0
  42. data/lib/new_relic/metric_parser/controller.rb +13 -5
  43. data/lib/new_relic/metric_parser/mem_cache.rb +1 -1
  44. data/lib/new_relic/metric_parser/other_transaction.rb +21 -0
  45. data/lib/new_relic/metric_spec.rb +3 -3
  46. data/lib/new_relic/noticed_error.rb +1 -1
  47. data/lib/new_relic/rack/developer_mode.rb +257 -0
  48. data/lib/new_relic/rack/metric_app.rb +56 -50
  49. data/lib/new_relic/rack/mongrel_rpm.ru +7 -6
  50. data/lib/new_relic/rack/newrelic.yml +4 -3
  51. data/lib/new_relic/rack_app.rb +2 -1
  52. data/lib/new_relic/recipes.rb +7 -7
  53. data/lib/new_relic/stats.rb +6 -14
  54. data/lib/new_relic/timer_lib.rb +27 -0
  55. data/lib/new_relic/transaction_analysis.rb +2 -7
  56. data/lib/new_relic/transaction_sample.rb +17 -85
  57. data/lib/new_relic/url_rule.rb +14 -0
  58. data/lib/new_relic/version.rb +3 -3
  59. data/lib/newrelic_rpm.rb +5 -9
  60. data/newrelic.yml +26 -9
  61. data/newrelic_rpm.gemspec +67 -32
  62. data/test/config/newrelic.yml +5 -0
  63. data/test/config/test_control.rb +6 -8
  64. data/test/new_relic/agent/active_record_instrumentation_test.rb +5 -6
  65. data/test/new_relic/agent/agent_controller_test.rb +18 -4
  66. data/test/new_relic/agent/agent_test_controller.rb +1 -6
  67. data/test/new_relic/agent/busy_calculator_test.rb +2 -0
  68. data/test/new_relic/agent/collection_helper_test.rb +6 -6
  69. data/test/new_relic/agent/error_collector_test.rb +13 -21
  70. data/test/new_relic/agent/metric_data_test.rb +3 -6
  71. data/test/new_relic/agent/rpm_agent_test.rb +121 -117
  72. data/test/new_relic/agent/task_instrumentation_test.rb +128 -133
  73. data/test/new_relic/agent/transaction_sample_test.rb +176 -170
  74. data/test/new_relic/agent/worker_loop_test.rb +24 -18
  75. data/test/new_relic/control_test.rb +13 -3
  76. data/test/new_relic/deployments_api_test.rb +7 -7
  77. data/test/new_relic/environment_test.rb +1 -1
  78. data/test/new_relic/metric_parser_test.rb +58 -4
  79. data/test/new_relic/rack/episodes_test.rb +317 -0
  80. data/test/new_relic/stats_test.rb +3 -2
  81. data/test/test_contexts.rb +28 -0
  82. data/test/test_helper.rb +24 -5
  83. data/ui/helpers/{newrelic_helper.rb → developer_mode_helper.rb} +63 -23
  84. data/ui/views/layouts/newrelic_default.rhtml +6 -6
  85. data/ui/views/newrelic/_explain_plans.rhtml +4 -4
  86. data/ui/views/newrelic/_sample.rhtml +18 -17
  87. data/ui/views/newrelic/_segment.rhtml +1 -0
  88. data/ui/views/newrelic/_segment_row.rhtml +8 -8
  89. data/ui/views/newrelic/_show_sample_detail.rhtml +1 -1
  90. data/ui/views/newrelic/_show_sample_sql.rhtml +2 -2
  91. data/ui/views/newrelic/_sql_row.rhtml +8 -3
  92. data/ui/views/newrelic/_stack_trace.rhtml +9 -24
  93. data/ui/views/newrelic/_table.rhtml +1 -1
  94. data/ui/views/newrelic/explain_sql.rhtml +3 -2
  95. data/ui/views/newrelic/{images → file/images}/arrow-close.png +0 -0
  96. data/ui/views/newrelic/{images → file/images}/arrow-open.png +0 -0
  97. data/ui/views/newrelic/{images → file/images}/blue_bar.gif +0 -0
  98. data/ui/views/newrelic/{images → file/images}/file_icon.png +0 -0
  99. data/ui/views/newrelic/{images → file/images}/gray_bar.gif +0 -0
  100. data/ui/views/newrelic/{images → file/images}/new-relic-rpm-desktop.gif +0 -0
  101. data/ui/views/newrelic/{images → file/images}/new_relic_rpm_desktop.gif +0 -0
  102. data/ui/views/newrelic/{images → file/images}/textmate.png +0 -0
  103. data/ui/views/newrelic/file/javascript/jquery-1.4.2.js +6240 -0
  104. data/ui/views/newrelic/file/javascript/transaction_sample.js +120 -0
  105. data/ui/views/newrelic/{stylesheets → file/stylesheets}/style.css +0 -0
  106. data/ui/views/newrelic/index.rhtml +7 -5
  107. data/ui/views/newrelic/sample_not_found.rhtml +1 -1
  108. data/ui/views/newrelic/show_sample.rhtml +5 -6
  109. metadata +92 -34
  110. data/README.md +0 -138
  111. data/lib/new_relic/commands/new_relic_commands.rb +0 -30
  112. data/lib/new_relic/control/rails.rb +0 -151
  113. data/test/new_relic/agent/mock_ar_connection.rb +0 -40
  114. data/test/ui/newrelic_controller_test.rb +0 -14
  115. data/test/ui/newrelic_helper_test.rb +0 -53
  116. data/ui/controllers/newrelic_controller.rb +0 -220
  117. data/ui/views/newrelic/javascript/prototype-scriptaculous.js +0 -7288
  118. data/ui/views/newrelic/javascript/transaction_sample.js +0 -107
@@ -22,21 +22,32 @@ module NewRelic
22
22
  # include NewRelic::Agent::Instrumentation::Rack
23
23
  # end
24
24
  #
25
- # == Instrumenting Metal
25
+ # == Instrumenting Metal and Cascading Middlewares
26
26
  #
27
- # If you are using Metal, be sure and extend the your Metal class with the
28
- # Rack instrumentation:
27
+ # Metal apps and apps belonging to Rack::Cascade middleware
28
+ # follow a convention of returning a 404 for all requests except
29
+ # the ones they are set up to handle. This means that New Relic
30
+ # needs to ignore these calls when they return a 404.
29
31
  #
30
- # require 'newrelic_rpm'
31
- # require 'new_relic/agent/instrumentation/rack'
32
+ # In these cases, you should not include or extend the Rack
33
+ # module but instead include
34
+ # NewRelic::Agent::Instrumentation::ControllerInstrumentation.
35
+ # Here's how that might look for a Metal app:
36
+ #
37
+ # require 'new_relic/agent/instrumentation/controller_instrumentation'
32
38
  # class MetalApp
39
+ # extend NewRelic::Agent::Instrumentation::ControllerInstrumentation
33
40
  # def self.call(env)
34
- # ...
41
+ # if should_do_my_thing?
42
+ # perform_action_with_newrelic_trace(:category => :rack) do
43
+ # return my_response(env)
44
+ # end
45
+ # else
46
+ # return [404, {"Content-Type" => "text/html"}, ["Not Found"]]
47
+ # end
35
48
  # end
36
- # # Do the include after the call method is defined:
37
- # extend NewRelic::Agent::Instrumentation::Rack
38
49
  # end
39
- #
50
+ #
40
51
  # == Overriding the metric name
41
52
  #
42
53
  # By default the middleware is identified only by its class, but if you want to
@@ -53,31 +64,6 @@ module NewRelic
53
64
  # add_transaction_tracer :call, :category => :rack, :name => 'my app'
54
65
  # end
55
66
  #
56
- # == Cascading or chained calls
57
- #
58
- # Calls which return a 404 will not have transactions recorded, but
59
- # any calls to instrumented frameworks like ActiveRecord will still be
60
- # captured even if the result is a 404. To avoid this you need to
61
- # instrument only when you are the endpoint.
62
- #
63
- # In these cases, you should not include or extend the Rack module but instead
64
- # include NewRelic::Agent::Instrumentation::ControllerInstrumentation.
65
- # Here's how that might look:
66
- #
67
- # require 'new_relic/agent/instrumentation/controller_instrumentation'
68
- # class MetalApp
69
- # extend NewRelic::Agent::Instrumentation::ControllerInstrumentation
70
- # def self.call(env)
71
- # if should_do_my_thing?
72
- # perform_action_with_newrelic_trace(:category => :rack) do
73
- # return my_response(env)
74
- # end
75
- # else
76
- # return [404, {"Content-Type" => "text/html"}, ["Not Found"]]
77
- # end
78
- # end
79
- # end
80
- #
81
67
  module Rack
82
68
  def newrelic_request_headers
83
69
  @newrelic_request.env
@@ -67,7 +67,7 @@ module Agent
67
67
  #
68
68
  def trace_execution_unscoped(metric_names, options={})
69
69
  return yield unless NewRelic::Agent.is_execution_traced?
70
- t0 = Time.now.to_f
70
+ t0 = Time.now
71
71
  stats = Array(metric_names).map do | metric_name |
72
72
  NewRelic::Agent.instance.stats_engine.get_stats_no_scope metric_name
73
73
  end
@@ -76,7 +76,7 @@ module Agent
76
76
  yield
77
77
  ensure
78
78
  NewRelic::Agent.instance.pop_trace_execution_flag if options[:force]
79
- duration = Time.now.to_f - t0 # for some reason this is 3 usec faster than Time - Time
79
+ duration = (Time.now - t0).to_f # for some reason this is 3 usec faster than Time - Time
80
80
  stats.each { |stat| stat.trace_call(duration) }
81
81
  end
82
82
  end
@@ -110,7 +110,7 @@ module Agent
110
110
  produce_metric = options[:metric] != false
111
111
  deduct_call_time_from_parent = options[:deduct_call_time_from_parent] != false
112
112
  scoped_metric_only = produce_metric && options[:scoped_metric_only]
113
- t0 = Time.now.to_f
113
+ t0 = Time.now
114
114
  if metric_names.instance_of? Array
115
115
  first_name = metric_names.first
116
116
  metric_stats = []
@@ -131,7 +131,7 @@ module Agent
131
131
  # Keep a reference to the scope we are pushing so we can do a sanity check making
132
132
  # sure when we pop we get the one we 'expected'
133
133
  NewRelic::Agent.instance.push_trace_execution_flag(true) if options[:force]
134
- expected_scope = NewRelic::Agent.instance.stats_engine.push_scope(first_name, t0, deduct_call_time_from_parent)
134
+ expected_scope = NewRelic::Agent.instance.stats_engine.push_scope(first_name, t0.to_f, deduct_call_time_from_parent)
135
135
  rescue => e
136
136
  NewRelic::Control.instance.log.error("Caught exception in trace_method_execution header. Metric name = #{first_name}, exception = #{e}")
137
137
  NewRelic::Control.instance.log.error(e.backtrace.join("\n"))
@@ -140,13 +140,13 @@ module Agent
140
140
  begin
141
141
  yield
142
142
  ensure
143
- t1 = Time.now.to_f
144
- duration = t1 - t0
143
+ t1 = Time.now
144
+ duration = (t1 - t0).to_f
145
145
 
146
146
  begin
147
147
  NewRelic::Agent.instance.pop_trace_execution_flag if options[:force]
148
148
  if expected_scope
149
- scope = NewRelic::Agent.instance.stats_engine.pop_scope expected_scope, duration, t1
149
+ scope = NewRelic::Agent.instance.stats_engine.pop_scope expected_scope, duration, t1.to_f
150
150
  exclusive = duration - scope.children_time
151
151
  metric_stats.each { |stats| stats.trace_call(duration, exclusive) }
152
152
  end
@@ -280,14 +280,14 @@ module Agent
280
280
  code = <<-CODE
281
281
  def #{_traced_method_name(method_name, metric_name_code)}(*args, &block)
282
282
  #{header}
283
- t0 = Time.now.to_f
283
+ t0 = Time.now
284
284
  stats = NewRelic::Agent.instance.stats_engine.get_stats_no_scope "#{metric_name_code}"
285
285
  begin
286
286
  #{"NewRelic::Agent.instance.push_trace_execution_flag(true)\n" if options[:force]}
287
287
  #{_untraced_method_name(method_name, metric_name_code)}(*args, &block)\n
288
288
  ensure
289
289
  #{"NewRelic::Agent.instance.pop_trace_execution_flag\n" if options[:force] }
290
- duration = Time.now.to_f - t0
290
+ duration = (Time.now - t0).to_f
291
291
  stats.trace_call(duration)
292
292
  #{options[:code_footer]}
293
293
  end
@@ -1,37 +1,37 @@
1
1
  module NewRelic
2
2
  module Agent
3
3
  module Samplers
4
- class DelayedJobLockSampler < NewRelic::Agent::Sampler
5
- def initialize
6
- super :delayed_job_lock
7
- raise Unsupported, "DJ instrumentation disabled" if NewRelic::Control.instance['disable_dj']
8
- raise Unsupported, "No DJ worker present" unless NewRelic::DelayedJobInjection.worker_name
9
- end
10
-
11
- def stats
12
- stats_engine.get_stats("Custom/DJ Locked Jobs", false)
13
- end
14
-
15
- def local_env
16
- NewRelic::Control.instance.local_env
17
- end
18
-
19
- def worker_name
20
- local_env.dispatcher_instance_id
21
- end
22
-
23
- def locked_jobs
24
- Delayed::Job.count(:conditions => {:locked_by => NewRelic::DelayedJobInjection.worker_name})
25
- end
26
-
27
- def self.supported_on_this_platform?
28
- defined?(Delayed::Job)
29
- end
30
-
31
- def poll
32
- stats.record_data_point locked_jobs
4
+ class DelayedJobLockSampler < NewRelic::Agent::Sampler
5
+ def initialize
6
+ super :delayed_job_lock
7
+ raise Unsupported, "DJ instrumentation disabled" if NewRelic::Control.instance['disable_dj']
8
+ raise Unsupported, "No DJ worker present" unless NewRelic::DelayedJobInjection.worker_name
9
+ end
10
+
11
+ def stats
12
+ stats_engine.get_stats("Custom/DJ Locked Jobs", false)
13
+ end
14
+
15
+ def local_env
16
+ NewRelic::Control.instance.local_env
17
+ end
18
+
19
+ def worker_name
20
+ local_env.dispatcher_instance_id
21
+ end
22
+
23
+ def locked_jobs
24
+ Delayed::Job.count(:conditions => {:locked_by => NewRelic::DelayedJobInjection.worker_name})
25
+ end
26
+
27
+ def self.supported_on_this_platform?
28
+ defined?(Delayed::Job)
29
+ end
30
+
31
+ def poll
32
+ stats.record_data_point locked_jobs
33
+ end
34
+ end
33
35
  end
34
36
  end
35
37
  end
36
- end
37
- end
@@ -24,15 +24,15 @@ module Agent
24
24
 
25
25
  # If use_scope is true, two chained metrics are created, one with scope and one without
26
26
  # If scoped_metric_only is true, only a scoped metric is created (used by rendering metrics which by definition are per controller only)
27
- def get_stats(metric_name, use_scope = true, scoped_metric_only = false)
28
-
27
+ def get_stats(metric_name, use_scope = true, scoped_metric_only = false, scope = nil)
28
+ scope ||= scope_name if use_scope
29
29
  if scoped_metric_only
30
- spec = NewRelic::MetricSpec.new metric_name, scope_name
30
+ spec = NewRelic::MetricSpec.new metric_name, scope
31
31
  stats = stats_hash[spec] ||= NewRelic::MethodTraceStats.new
32
32
  else
33
33
  stats = stats_hash[metric_name] ||= NewRelic::MethodTraceStats.new
34
- if use_scope && scope_name && scope_name != metric_name
35
- spec = NewRelic::MetricSpec.new metric_name, scope_name
34
+ if scope && scope != metric_name
35
+ spec = NewRelic::MetricSpec.new metric_name, scope
36
36
  scoped_stats = stats_hash[spec] ||= NewRelic::ScopedMethodTraceStats.new(stats)
37
37
  stats = scoped_stats
38
38
  end
@@ -45,6 +45,9 @@ module Agent
45
45
 
46
46
  # Add a sampler to be invoked just before each harvest.
47
47
  def add_harvest_sampler sampler
48
+ harvest_samplers.each do |s|
49
+ raise "Sampler #{sampler.id} is already registered. Don't call add_sampler directly anymore." if s.id == sampler.id
50
+ end
48
51
  harvest_samplers << sampler
49
52
  sampler.stats_engine = self
50
53
  log.debug "Adding harvest time sampler: #{sampler.id.to_s}"
@@ -33,6 +33,11 @@ module Agent
33
33
  b and b.sample_id
34
34
  end
35
35
 
36
+ def enable
37
+ @disabled = false
38
+ NewRelic::Agent.instance.stats_engine.transaction_sampler = self
39
+ end
40
+
36
41
  def disable
37
42
  @disabled = true
38
43
  NewRelic::Agent.instance.stats_engine.remove_transaction_sampler self
@@ -44,7 +49,7 @@ module Agent
44
49
  end
45
50
 
46
51
  def notice_first_scope_push(time)
47
- start_builder(time) unless disabled
52
+ start_builder(time.to_f) unless disabled
48
53
  end
49
54
 
50
55
  def notice_push_scope(scope, time=Time.now.to_f)
@@ -124,30 +129,41 @@ module Agent
124
129
  builder.set_transaction_cpu_time(cpu_time) if builder
125
130
  end
126
131
 
132
+ MAX_DATA_LENGTH = 16384
133
+ # duration is seconds, float value.
134
+ def notice_extra_data(message, duration, key, config=nil, config_key=nil)
135
+ return unless builder
136
+ segment = builder.current_segment
137
+ if segment
138
+ current_message = segment[key]
139
+ message = current_message + ";\n" + message if current_message
140
+ if message.length > (MAX_DATA_LENGTH - 4)
141
+ message = message[0..MAX_DATA_LENGTH - 4] + '...'
142
+ end
143
+
144
+ segment[key] = message
145
+ segment[config_key] = config if config_key
146
+ segment[:backtrace] = caller.join("\n") if duration >= @stack_trace_threshold
147
+ end
148
+ end
149
+
150
+ private :notice_extra_data
127
151
 
128
152
  # some statements (particularly INSERTS with large BLOBS
129
153
  # may be very large; we should trim them to a maximum usable length
130
154
  # config is the driver configuration for the connection
155
+ # duration is seconds, float value.
131
156
  MAX_SQL_LENGTH = 16384
132
157
  def notice_sql(sql, config, duration)
133
- return unless builder
134
158
  if Thread::current[:record_sql] != false
135
- segment = builder.current_segment
136
- if segment
137
- current_sql = segment[:sql]
138
- sql = current_sql + ";\n" + sql if current_sql
139
-
140
- if sql.length > (MAX_SQL_LENGTH - 4)
141
- sql = sql[0..MAX_SQL_LENGTH-4] + '...'
142
- end
143
-
144
- segment[:sql] = sql
145
- segment[:connection_config] = config
146
- segment[:backtrace] = caller.join("\n") if duration >= @stack_trace_threshold
147
- end
159
+ notice_extra_data(sql, duration, :sql, config, :connection_config)
148
160
  end
149
161
  end
150
162
 
163
+ # duration is seconds, float value.
164
+ def notice_nosql(key, duration)
165
+ notice_extra_data(key, duration, :key)
166
+ end
151
167
 
152
168
  # get the set of collected samples, merging into previous samples,
153
169
  # and clear the collected sample list.
@@ -9,6 +9,8 @@ module NewRelic
9
9
  def initialize
10
10
  @log = log
11
11
  @should_run = true
12
+ @next_invocation_time = Time.now
13
+ @period = 60.0
12
14
  end
13
15
 
14
16
  def lock
@@ -20,10 +22,10 @@ module NewRelic
20
22
  end
21
23
  # Run infinitely, calling the registered tasks at their specified
22
24
  # call periods. The caller is responsible for creating the thread
23
- # that runs this worker loop
24
- def run(period, &block)
25
- @period = period
26
- @next_invocation_time = Time.now + @period
25
+ # that runs this worker loop. This will run the task immediately.
26
+ def run(period=nil, &block)
27
+ @period = period if period
28
+ @next_invocation_time = Time.now
27
29
  @task = block
28
30
  while keep_running do
29
31
  now = Time.now
@@ -46,32 +48,31 @@ module NewRelic
46
48
  end
47
49
 
48
50
  def run_task
49
- lock.synchronize do
50
- @task.call
51
- end
52
- rescue ServerError => e
53
- log.debug "Server Error: #{e}"
54
- rescue NewRelic::Agent::ForceRestartException, NewRelic::Agent::ForceDisconnectException
55
- # blow out the loop
56
- raise
57
- rescue RuntimeError => e
58
- # This is probably a server error which has been logged in the server along
59
- # with your account name. Check and see if the agent listener is in the
60
- # stack trace and log it quietly if it is.
61
- message = "Error running task in worker loop, likely a server error (#{e})"
62
- if e.backtrace.grep(/agent_listener/).empty?
63
- log.error message
64
- else
65
- log.debug message
51
+ begin
52
+ lock.synchronize do
53
+ @task.call
54
+ end
55
+ rescue ServerError => e
56
+ log.debug "Server Error: #{e}"
57
+ rescue NewRelic::Agent::ForceRestartException, NewRelic::Agent::ForceDisconnectException
58
+ # blow out the loop
59
+ raise
60
+ rescue RuntimeError => e
61
+ # This is probably a server error which has been logged in the server along
62
+ # with your account name.
63
+ log.error "Error running task in worker loop, likely a server error (#{e})"
64
+ log.debug e.backtrace.join("\n")
65
+ rescue Timeout::Error, NewRelic::Agent::ServerConnectionException
66
+ # Want to ignore these because they are handled already
67
+ rescue SystemExit, NoMemoryError, SignalException
68
+ raise
69
+ rescue Exception => e
70
+ # Don't blow out the stack for anything that hasn't already propagated
71
+ log.error "Error running task in Agent Worker Loop '#{e}': #{e.backtrace.first}"
66
72
  log.debug e.backtrace.join("\n")
67
73
  end
68
- rescue Timeout::Error, NewRelic::Agent::ServerConnectionException
69
- # Want to ignore these because they are handled already
70
- rescue ScriptError, StandardError => e
71
- log.error "Error running task in Agent Worker Loop '#{e}': #{e.backtrace.first}"
72
- log.debug e.backtrace.join("\n")
73
- ensure
74
- while @next_invocation_time < Time.now
74
+ now = Time.now
75
+ while @next_invocation_time <= now && @period > 0
75
76
  @next_invocation_time += @period
76
77
  end
77
78
  end
@@ -1,5 +1,7 @@
1
1
  module NewRelic
2
2
  module CollectionHelper
3
+ DEFAULT_TRUNCATION_SIZE=256
4
+ DEFAULT_ARRAY_TRUNCATION_SIZE=1024
3
5
  # Transform parameter hash into a hash whose values are strictly
4
6
  # strings
5
7
  def normalize_params(params)
@@ -17,7 +19,7 @@ module NewRelic
17
19
  end
18
20
  new_params
19
21
  when Array
20
- params.first(20).map{|item| normalize_params(item)}
22
+ params.first(DEFAULT_ARRAY_TRUNCATION_SIZE).map{|item| normalize_params(item)}
21
23
  else
22
24
  truncate(flatten(params))
23
25
  end
@@ -50,7 +52,7 @@ module NewRelic
50
52
  else "#<#{object.class.to_s}>"
51
53
  end
52
54
  end
53
- def truncate(string, len=256)
55
+ def truncate(string, len=DEFAULT_TRUNCATION_SIZE)
54
56
  case string
55
57
  when Symbol then string
56
58
  when nil then ""
@@ -0,0 +1,85 @@
1
+ require 'optparse'
2
+
3
+ # Run the command given by the first argument. Right
4
+ # now all we have is deployments. We hope to have other
5
+ # kinds of events here later.
6
+ $LOAD_PATH << "#{File.dirname(__FILE__)}/.."
7
+ module NewRelic
8
+ class Command
9
+ attr_accessor :leftover
10
+ # Capture a failure to execute the command.
11
+ class CommandFailure < StandardError
12
+ attr_reader :options
13
+ def initialize message, opt_parser=nil
14
+ super message
15
+ @options = opt_parser
16
+ end
17
+ end
18
+
19
+ def info(message)
20
+ STDOUT.puts message
21
+ end
22
+
23
+ def err(message)
24
+ STDERR.puts message
25
+ end
26
+
27
+ def initialize(command_line_args)
28
+ if Hash === command_line_args
29
+ # command line args is an options hash
30
+ command_line_args.each do | key, value |
31
+ instance_variable_set "@#{key}", value.to_s if value
32
+ end
33
+ else
34
+ # parse command line args. Throw an exception on a bad arg.
35
+ @options = options do | opts |
36
+ opts.on("-h", "Show this help") { raise CommandFailure, opts.to_s }
37
+ end
38
+ @leftover = @options.parse(command_line_args)
39
+ end
40
+ rescue OptionParser::ParseError => e
41
+ raise CommandFailure.new e.message, @options
42
+ end
43
+
44
+ @commands = []
45
+ def self.inherited(subclass)
46
+ @commands << subclass
47
+ end
48
+
49
+ cmds = File.expand_path(File.join(File.dirname(__FILE__), 'commands', '*.rb'))
50
+ Dir[cmds].each{|command| require command }
51
+
52
+ def self.run
53
+
54
+ @command_names = @commands.map(&:command)
55
+
56
+ extra = []
57
+ options = ARGV.options do |opts|
58
+ script_name = File.basename($0)
59
+ if script_name =~ /newrelic_cmd$/
60
+ $stdout.puts "warning: the 'newrelic_cmd' script has been renamed 'newrelic'"
61
+ script_name = 'newrelic'
62
+ end
63
+ opts.banner = "Usage: #{script_name} [ #{ @command_names.join(" | ")} ] [options]"
64
+ opts.separator "use '#{script_name} <command> -h' to see detailed command options"
65
+ opts
66
+ end
67
+ extra = options.order!
68
+ command = extra.shift
69
+ # just make it a little easier on them
70
+ command = 'deployments' if command =~ /deploy/
71
+ if command.nil?
72
+ STDERR.puts options
73
+ elsif !@command_names.include?(command)
74
+ STDERR.puts "Unrecognized command: #{command}"
75
+ STDERR.puts options
76
+ else
77
+ command_class = @commands.find{ |c| c.command == command}
78
+ command_class.new(extra).run
79
+ end
80
+ rescue OptionParser::InvalidOption => e
81
+ raise NewRelic::Command::CommandFailure, e.message
82
+ end
83
+ end
84
+
85
+ end