scout_rails 1.0.7 → 1.0.8.pre.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,8 @@
1
+ # 1.0.8
2
+
3
+ * Processing metrics when a process starts + exits to prevent losing in-memory metrics on process exit.
4
+ * Ensuring scope is nil for samplers (could be run when a process is killed before scope is reset)
5
+
1
6
  # 1.0.7
2
7
 
3
8
  * Sinatra 1.3+ compatibility (alias dispatch! instead of route_eval)
@@ -13,6 +13,7 @@ require File.expand_path('../scout_rails/agent/reporting.rb', __FILE__)
13
13
  require File.expand_path('../scout_rails/layaway.rb', __FILE__)
14
14
  require File.expand_path('../scout_rails/layaway_file.rb', __FILE__)
15
15
  require File.expand_path('../scout_rails/config.rb', __FILE__)
16
+ require File.expand_path('../scout_rails/background_worker.rb', __FILE__)
16
17
  require File.expand_path('../scout_rails/environment.rb', __FILE__)
17
18
  require File.expand_path('../scout_rails/metric_meta.rb', __FILE__)
18
19
  require File.expand_path('../scout_rails/metric_stats.rb', __FILE__)
@@ -63,27 +63,46 @@ module ScoutRails
63
63
  @started = true
64
64
  logger.info "Starting monitoring. Framework [#{environment.framework}] App Server [#{environment.app_server}]."
65
65
  start_instruments
66
- if !start_worker_thread?
66
+ if !start_background_worker?
67
67
  logger.debug "Not starting worker thread"
68
- install_passenger_worker_process_event if environment.app_server == :passenger
68
+ install_passenger_events if environment.app_server == :passenger
69
69
  install_unicorn_worker_loop if environment.app_server == :unicorn
70
70
  return
71
71
  end
72
- start_worker_thread
72
+ start_background_worker
73
73
  handle_exit
74
74
  logger.info "Scout Agent [#{ScoutRails::VERSION}] Initialized"
75
75
  end
76
76
 
77
- # Placeholder: store metrics locally on exit so those in memory aren't lost. Need to decide
78
- # whether we'll report these immediately or just store locally and risk having stale data.
77
+ # at_exit, calls Agent#shutdown to wrapup metric reporting.
79
78
  def handle_exit
80
79
  if environment.sinatra? || environment.jruby? || environment.rubinius?
81
80
  logger.debug "Exit handler not supported"
82
81
  else
83
- at_exit { at_exit { logger.debug "Shutdown!" } }
82
+ at_exit do
83
+ logger.debug "Shutdown!"
84
+ # MRI 1.9 bug drops exit codes.
85
+ # http://bugs.ruby-lang.org/issues/5218
86
+ if environment.ruby_19?
87
+ status = $!.status if $!.is_a?(SystemExit)
88
+ shutdown
89
+ exit status if status
90
+ else
91
+ shutdown
92
+ end
93
+ end # at_exit
84
94
  end
85
95
  end
86
96
 
97
+ # Called via an at_exit handler, it (1) stops the background worker and (2) runs it a final time.
98
+ # The final run ensures metrics are stored locally to the layaway / reported to scoutapp.com. Otherwise,
99
+ # in-memory metrics would be lost and a gap would appear on restarts.
100
+ def shutdown
101
+ return if !started?
102
+ @background_worker.stop
103
+ @background_worker.run_once
104
+ end
105
+
87
106
  def started?
88
107
  @started
89
108
  end
@@ -96,14 +115,20 @@ module ScoutRails
96
115
  # * A supported application server isn't detected (example: running via Rails console)
97
116
  # * A supported application server is detected, but it forks (Passenger). In this case,
98
117
  # the agent is started in the forked process.
99
- def start_worker_thread?
118
+ def start_background_worker?
100
119
  !environment.forking? or environment.app_server == :thin
101
120
  end
102
121
 
103
- def install_passenger_worker_process_event
122
+ def install_passenger_events
104
123
  PhusionPassenger.on_event(:starting_worker_process) do |forked|
105
124
  logger.debug "Passenger is starting a worker process. Starting worker thread."
106
- self.class.instance.start_worker_thread
125
+ self.class.instance.start_background_worker
126
+ end
127
+ # The agent's at_exit hook doesn't run when a Passenger process stops.
128
+ # This does run when a process stops.
129
+ PhusionPassenger.on_event(:stopping_worker_process) do
130
+ logger.debug "Passenger is stopping a worker process, shutting down the agent."
131
+ ScoutRails::Agent.instance.shutdown
107
132
  end
108
133
  end
109
134
 
@@ -112,52 +137,29 @@ module ScoutRails
112
137
  Unicorn::HttpServer.class_eval do
113
138
  old = instance_method(:worker_loop)
114
139
  define_method(:worker_loop) do |worker|
115
- ScoutRails::Agent.instance.start_worker_thread
140
+ ScoutRails::Agent.instance.start_background_worker
116
141
  old.bind(self).call(worker)
117
142
  end
118
143
  end
119
144
  end
120
145
 
121
- # in seconds, time between when the worker thread wakes up and runs.
122
- def period
123
- 60
124
- end
125
-
126
146
  # Creates the worker thread. The worker thread is a loop that runs continuously. It sleeps for +Agent#period+ and when it wakes,
127
147
  # processes data, either saving it to disk or reporting to Scout.
128
- def start_worker_thread
148
+ def start_background_worker
129
149
  logger.debug "Creating worker thread."
130
- @worker_thread = Thread.new do
131
- begin
132
- logger.debug "Starting worker thread, running every #{period} seconds"
133
- next_time = Time.now + period
134
- while true do
135
- now = Time.now
136
- while now < next_time
137
- sleep_time = next_time - now
138
- sleep(sleep_time) if sleep_time > 0
139
- now = Time.now
140
- end
141
- process_metrics
142
- while next_time <= now
143
- next_time += period
144
- end
145
- end
146
- rescue
147
- logger.debug "Worker Thread Exception!!!!!!!"
148
- logger.debug $!.message
149
- logger.debug $!.backtrace
150
- end
150
+ @background_worker = ScoutRails::BackgroundWorker.new
151
+ @background_worker_thread = Thread.new do
152
+ @background_worker.start { process_metrics }
151
153
  end # thread new
152
154
  logger.debug "Done creating worker thread."
153
155
  end
154
156
 
155
- # Called from #process_metrics, which is run via the worker thread.
157
+ # Called from #process_metrics, which is run via the background worker.
156
158
  def run_samplers
157
159
  begin
158
160
  cpu_util=@process_cpu.run # returns a hash
159
161
  logger.debug "Process CPU: #{cpu_util.inspect} [#{environment.processors} CPU(s)]"
160
- store.track!("CPU/Utilization",cpu_util) if cpu_util
162
+ store.track!("CPU/Utilization",cpu_util,:scope => nil) if cpu_util
161
163
  rescue => e
162
164
  logger.info "Error reading ProcessCpu"
163
165
  logger.debug e.message
@@ -167,7 +169,7 @@ module ScoutRails
167
169
  begin
168
170
  mem_usage=@process_memory.run # returns a single number, in MB
169
171
  logger.debug "Process Memory: #{mem_usage}MB"
170
- store.track!("Memory/Physical",mem_usage) if mem_usage
172
+ store.track!("Memory/Physical",mem_usage,:scope => nil) if mem_usage
171
173
  rescue => e
172
174
  logger.info "Error reading ProcessMemory"
173
175
  logger.debug e.message
@@ -0,0 +1,43 @@
1
+ # Used to run a given task every 60 seconds.
2
+ class ScoutRails::BackgroundWorker
3
+ # in seconds, time between when the worker thread wakes up and runs.
4
+ PERIOD = 60
5
+
6
+ def initialize
7
+ @keep_running = true
8
+ end
9
+
10
+ def stop
11
+ @keep_running = false
12
+ end
13
+
14
+ # Runs the task passed to +start+ once.
15
+ def run_once
16
+ @task.call if @task
17
+ end
18
+
19
+ # Starts running the passed block every 60 seconds (starting now).
20
+ def start(&block)
21
+ @task = block
22
+ begin
23
+ ScoutRails::Agent.instance.logger.debug "Starting Background Worker, running every #{PERIOD} seconds"
24
+ next_time = Time.now
25
+ while @keep_running do
26
+ now = Time.now
27
+ while now < next_time
28
+ sleep_time = next_time - now
29
+ sleep(sleep_time) if sleep_time > 0
30
+ now = Time.now
31
+ end
32
+ @task.call
33
+ while next_time <= now
34
+ next_time += PERIOD
35
+ end
36
+ end
37
+ rescue
38
+ ScoutRails::Agent.instance.logger.debug "Background Worker Exception!!!!!!!"
39
+ ScoutRails::Agent.instance.logger.debug $!.message
40
+ ScoutRails::Agent.instance.logger.debug $!.backtrace
41
+ end
42
+ end
43
+ end
@@ -104,6 +104,10 @@ module ScoutRails
104
104
  defined?(JRuby)
105
105
  end
106
106
 
107
+ def ruby_19?
108
+ defined?(RUBY_ENGINE) && RUBY_ENGINE == "ruby" && RUBY_VERSION.match(/^1\.9/)
109
+ end
110
+
107
111
  ### framework checks
108
112
 
109
113
  def sinatra?
@@ -1,3 +1,3 @@
1
1
  module ScoutRails
2
- VERSION = "1.0.7"
2
+ VERSION = "1.0.8.pre.3"
3
3
  end
metadata CHANGED
@@ -1,8 +1,8 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: scout_rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.7
5
- prerelease:
4
+ version: 1.0.8.pre.3
5
+ prerelease: 6
6
6
  platform: ruby
7
7
  authors:
8
8
  - Derek Haynes
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2012-11-20 00:00:00.000000000 Z
13
+ date: 2012-11-27 00:00:00.000000000 Z
14
14
  dependencies: []
15
15
  description: Monitors a Ruby on Rails application and reports detailed metrics on
16
16
  performance to Scout, a hosted monitoring service.
@@ -30,6 +30,7 @@ files:
30
30
  - lib/scout_rails/agent.rb
31
31
  - lib/scout_rails/agent/logging.rb
32
32
  - lib/scout_rails/agent/reporting.rb
33
+ - lib/scout_rails/background_worker.rb
33
34
  - lib/scout_rails/config.rb
34
35
  - lib/scout_rails/environment.rb
35
36
  - lib/scout_rails/instruments/active_record_instruments.rb
@@ -66,9 +67,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
66
67
  required_rubygems_version: !ruby/object:Gem::Requirement
67
68
  none: false
68
69
  requirements:
69
- - - ! '>='
70
+ - - ! '>'
70
71
  - !ruby/object:Gem::Version
71
- version: '0'
72
+ version: 1.3.1
72
73
  requirements: []
73
74
  rubyforge_project: scout_rails
74
75
  rubygems_version: 1.8.10