scout_rails 1.0.7 → 1.0.8.pre.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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