scout_rails 1.0.5.pre → 1.0.5

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.markdown CHANGED
@@ -1,8 +1,11 @@
1
- # 1.0.5.pre
1
+ # 1.0.5
2
2
 
3
3
  * Removing duplicate Enviornment#unicorn? method
4
4
  * Removing logging when not instrumenting unscoped method (confusing - looks like an error)
5
5
  * Recording ActiveRecord exists queries as MODEL#exists vs. SQL#UNKNOWN
6
+ * Handling log_level config option and defaulting to 'info' instead of 'debug'
7
+ * Not crashing the app when log file isn't writeable
8
+ * Handling the :reset directive. Resets the metric_lookup when provided.
6
9
 
7
10
  # 1.0.4
8
11
 
@@ -0,0 +1,44 @@
1
+ # Contains methods specific to logging (initializing the log file, applying the log level, applying the log format, etc.)
2
+ module ScoutRails
3
+ class Agent
4
+ module Logging
5
+ def log_path
6
+ "#{environment.root}/log"
7
+ end
8
+
9
+ def init_logger
10
+ @log_file = "#{log_path}/scout_rails.log"
11
+ begin
12
+ @logger = Logger.new(@log_file)
13
+ @logger.level = log_level
14
+ apply_log_format
15
+ rescue Exception => e
16
+ @logger = Logger.new(STDOUT)
17
+ apply_log_format
18
+ @logger.error "Unable to access log file: #{e.message}"
19
+ end
20
+ @logger
21
+ end
22
+
23
+ def apply_log_format
24
+ def logger.format_message(severity, timestamp, progname, msg)
25
+ # since STDOUT isn't exclusive like the scout_rails.log file, apply a prefix.
26
+ prefix = @logdev.dev == STDOUT ? "scout_rails " : ''
27
+ prefix + "[#{timestamp.strftime("%m/%d/%y %H:%M:%S %z")} #{Socket.gethostname} (#{$$})] #{severity} : #{msg}\n"
28
+ end
29
+ end
30
+
31
+ def log_level
32
+ case config.settings['log_level'].downcase
33
+ when "debug" then Logger::DEBUG
34
+ when "info" then Logger::INFO
35
+ when "warn" then Logger::WARN
36
+ when "error" then Logger::ERROR
37
+ when "fatal" then Logger::FATAL
38
+ else Logger::INFO
39
+ end
40
+ end
41
+ end # module Logging
42
+ include Logging
43
+ end # class Agent
44
+ end # moudle ScoutRails
@@ -0,0 +1,97 @@
1
+ # Methods related to sending metrics to scoutapp.com.
2
+ module ScoutRails
3
+ class Agent
4
+ module Reporting
5
+ # Called in the worker thread. Merges in-memory metrics w/those on disk and reports metrics
6
+ # to the server.
7
+ def process_metrics
8
+ logger.debug "Processing metrics"
9
+ run_samplers
10
+ metrics = layaway.deposit_and_deliver
11
+ if metrics.any?
12
+ add_metric_ids(metrics)
13
+ # for debugging, count the total number of requests
14
+ controller_count = 0
15
+ metrics.each do |meta,stats|
16
+ if meta.metric_name =~ /\AController/
17
+ controller_count += stats.call_count
18
+ end
19
+ end
20
+ logger.debug "#{config.settings['name']} Delivering metrics for #{controller_count} requests."
21
+ response = post( checkin_uri,
22
+ Marshal.dump(:metrics => metrics, :sample => store.sample),
23
+ "Content-Type" => "application/json" )
24
+ if response and response.is_a?(Net::HTTPSuccess)
25
+ directives = Marshal.load(response.body)
26
+ self.metric_lookup.merge!(directives[:metric_lookup])
27
+ if directives[:reset]
28
+ logger.info "Resetting metric_lookup."
29
+ self.metric_lookup = Hash.new
30
+ end
31
+ store.transaction_sample_lock.synchronize do
32
+ store.sample = nil
33
+ end
34
+ logger.debug "Metric Cache Size: #{metric_lookup.size}"
35
+ end
36
+ end
37
+ rescue
38
+ logger.info "Error on checkin to #{checkin_uri.to_s}"
39
+ logger.info $!.message
40
+ logger.debug $!.backtrace
41
+ end
42
+
43
+ # Before reporting, lookup metric_id for each MetricMeta. This speeds up
44
+ # reporting on the server-side.
45
+ def add_metric_ids(metrics)
46
+ metrics.each do |meta,stats|
47
+ if metric_id = metric_lookup[meta]
48
+ meta.metric_id = metric_id
49
+ end
50
+ end
51
+ end
52
+
53
+ def checkin_uri
54
+ URI.parse("http://#{config.settings['host']}/app/#{config.settings['key']}/checkin.scout?name=#{CGI.escape(config.settings['name'])}")
55
+ end
56
+
57
+ def post(url, body, headers = Hash.new)
58
+ response = nil
59
+ request(url) do |connection|
60
+ post = Net::HTTP::Post.new( url.path +
61
+ (url.query ? ('?' + url.query) : ''),
62
+ HTTP_HEADERS.merge(headers) )
63
+ post.body = body
64
+ response=connection.request(post)
65
+ end
66
+ response
67
+ end
68
+
69
+ def request(url, &connector)
70
+ response = nil
71
+ response = http(url).start(&connector)
72
+ logger.debug "got response: #{response.inspect}"
73
+ case response
74
+ when Net::HTTPSuccess, Net::HTTPNotModified
75
+ logger.debug "/checkin OK"
76
+ when Net::HTTPBadRequest
77
+ logger.warn "/checkin FAILED: The Account Key [#{config.settings['key']}] is invalid."
78
+ else
79
+ logger.debug "/checkin FAILED: #{response.inspect}"
80
+ end
81
+ rescue Exception
82
+ logger.debug "Exception sending request to server: #{$!.message}"
83
+ ensure
84
+ response
85
+ end
86
+
87
+ # Take care of the http proxy, if specified in config.
88
+ # Given a blank string, the proxy_uri URI instance's host/port/user/pass will be nil.
89
+ # Net::HTTP::Proxy returns a regular Net::HTTP class if the first argument (host) is nil.
90
+ def http(url)
91
+ proxy_uri = URI.parse(config.settings['proxy'].to_s)
92
+ Net::HTTP::Proxy(proxy_uri.host,proxy_uri.port,proxy_uri.user,proxy_uri.password).new(url.host, url.port)
93
+ end
94
+ end # module Reporting
95
+ include Reporting
96
+ end # class Agent
97
+ end # module ScoutRails
@@ -7,7 +7,6 @@ module ScoutRails
7
7
  class Agent
8
8
  # Headers passed up with all API requests.
9
9
  HTTP_HEADERS = { "Agent-Hostname" => Socket.gethostname }
10
- DEFAULT_HOST = 'scoutapp.com'
11
10
  # see self.instance
12
11
  @@instance = nil
13
12
 
@@ -93,16 +92,6 @@ module ScoutRails
93
92
  File.expand_path(File.join("..","..",".."), __FILE__)
94
93
  end
95
94
 
96
- def init_logger
97
- @log_file = "#{log_path}/scout_rails.log"
98
- @logger = Logger.new(@log_file)
99
- @logger.level = Logger::DEBUG
100
- def logger.format_message(severity, timestamp, progname, msg)
101
- prefix = "[#{timestamp.strftime("%m/%d/%y %H:%M:%S %z")} #{Socket.gethostname} (#{$$})] #{severity} : #{msg}\n"
102
- end
103
- @logger
104
- end
105
-
106
95
  # The worker thread will automatically start UNLESS:
107
96
  # * A supported application server isn't detected (example: running via Rails console)
108
97
  # * A supported application server is detected, but it forks (Passenger). In this case,
@@ -129,10 +118,6 @@ module ScoutRails
129
118
  end
130
119
  end
131
120
 
132
- def log_path
133
- "#{environment.root}/log"
134
- end
135
-
136
121
  # in seconds, time between when the worker thread wakes up and runs.
137
122
  def period
138
123
  60
@@ -167,26 +152,6 @@ module ScoutRails
167
152
  logger.debug "Done creating worker thread."
168
153
  end
169
154
 
170
- # Writes each payload to a file for auditing.
171
- def write_to_file(object)
172
- logger.debug "Writing to file"
173
- full_path = Pathname.new(RAILS_ROOT+'/log/audit/scout')
174
- ( full_path +
175
- "#{Time.now.strftime('%Y-%m-%d_%H:%M:%S')}.json" ).open("w") do |f|
176
- f.puts object.to_json
177
- end
178
- end
179
-
180
- # Before reporting, lookup metric_id for each MetricMeta. This speeds up
181
- # reporting on the server-side.
182
- def add_metric_ids(metrics)
183
- metrics.each do |meta,stats|
184
- if metric_id = metric_lookup[meta]
185
- meta.metric_id = metric_id
186
- end
187
- end
188
- end
189
-
190
155
  # Called from #process_metrics, which is run via the worker thread.
191
156
  def run_samplers
192
157
  begin
@@ -210,82 +175,6 @@ module ScoutRails
210
175
  end
211
176
  end
212
177
 
213
- # Called in the worker thread. Merges in-memory metrics w/those on disk and reports metrics
214
- # to the server.
215
- def process_metrics
216
- logger.debug "Processing metrics"
217
- run_samplers
218
- metrics = layaway.deposit_and_deliver
219
- if metrics.any?
220
- add_metric_ids(metrics)
221
- # for debugging, count the total number of requests
222
- controller_count = 0
223
- metrics.each do |meta,stats|
224
- if meta.metric_name =~ /\AController/
225
- controller_count += stats.call_count
226
- end
227
- end
228
- logger.debug "#{config.settings['name']} Delivering metrics for #{controller_count} requests."
229
- response = post( checkin_uri,
230
- Marshal.dump(:metrics => metrics, :sample => store.sample),
231
- "Content-Type" => "application/json" )
232
- if response and response.is_a?(Net::HTTPSuccess)
233
- directives = Marshal.load(response.body)
234
- self.metric_lookup.merge!(directives[:metric_lookup])
235
- store.transaction_sample_lock.synchronize do
236
- store.sample = nil
237
- end
238
- logger.debug "Metric Cache Size: #{metric_lookup.size}"
239
- end
240
- end
241
- rescue
242
- logger.info "Error on checkin to #{checkin_uri.to_s}"
243
- logger.info $!.message
244
- logger.debug $!.backtrace
245
- end
246
-
247
- def checkin_uri
248
- URI.parse("http://#{config.settings['host'] || DEFAULT_HOST}/app/#{config.settings['key']}/checkin.scout?name=#{CGI.escape(config.settings['name'])}")
249
- end
250
-
251
- def post(url, body, headers = Hash.new)
252
- response = nil
253
- request(url) do |connection|
254
- post = Net::HTTP::Post.new( url.path +
255
- (url.query ? ('?' + url.query) : ''),
256
- HTTP_HEADERS.merge(headers) )
257
- post.body = body
258
- response=connection.request(post)
259
- end
260
- response
261
- end
262
-
263
- def request(url, &connector)
264
- response = nil
265
- response = http(url).start(&connector)
266
- logger.debug "got response: #{response.inspect}"
267
- case response
268
- when Net::HTTPSuccess, Net::HTTPNotModified
269
- logger.debug "/checkin OK"
270
- when Net::HTTPBadRequest
271
- logger.warn "/checkin FAILED: The Account Key [#{config.settings['key']}] is invalid."
272
- else
273
- logger.debug "/checkin FAILED: #{response.inspect}"
274
- end
275
- rescue Exception
276
- logger.debug "Exception sending request to server: #{$!.message}"
277
- ensure
278
- response
279
- end
280
-
281
- # Take care of the http proxy, if specified in config.
282
- # Given a blank string, the proxy_uri URI instance's host/port/user/pass will be nil.
283
- # Net::HTTP::Proxy returns a regular Net::HTTP class if the first argument (host) is nil.
284
- def http(url)
285
- proxy_uri = URI.parse(config.settings['proxy'].to_s)
286
- Net::HTTP::Proxy(proxy_uri.host,proxy_uri.port,proxy_uri.user,proxy_uri.password).new(url.host, url.port)
287
- end
288
-
289
178
  # Loads the instrumention logic.
290
179
  def load_instruments
291
180
  case environment.framework
@@ -1,5 +1,10 @@
1
1
  module ScoutRails
2
- class Config
2
+ class Config
3
+ DEFAULTS = {
4
+ 'host' => 'scoutapp.com',
5
+ 'log_level' => 'info'
6
+ }
7
+
3
8
  def initialize(config_path = nil)
4
9
  @config_path = config_path
5
10
  end
@@ -18,17 +23,20 @@ module ScoutRails
18
23
  end
19
24
 
20
25
  def load_file
21
- if !File.exist?(config_file)
22
- ScoutRails::Agent.instance.logger.warn "No config file found at [#{config_file}]."
26
+ begin
27
+ if !File.exist?(config_file)
28
+ ScoutRails::Agent.instance.logger.warn "No config file found at [#{config_file}]."
29
+ @settings = {}
30
+ else
31
+ @settings = YAML.load(ERB.new(File.read(config_file)).result(binding))[ScoutRails::Agent.instance.environment.env] || {}
32
+ end
33
+ rescue Exception => e
34
+ ScoutRails::Agent.instance.logger.warn "Unable to load the config file."
35
+ ScoutRails::Agent.instance.logger.warn e.message
36
+ ScoutRails::Agent.instance.logger.warn e.backtrace
23
37
  @settings = {}
24
- else
25
- @settings = YAML.load(ERB.new(File.read(config_file)).result(binding))[ScoutRails::Agent.instance.environment.env] || {}
26
- end
27
- rescue Exception => e
28
- ScoutRails::Agent.instance.logger.warn "Unable to load the config file."
29
- ScoutRails::Agent.instance.logger.warn e.message
30
- ScoutRails::Agent.instance.logger.warn e.backtrace
31
- @settings = {}
38
+ end
39
+ @settings = DEFAULTS.merge(@settings)
32
40
  end
33
- end
34
- end
41
+ end # Config
42
+ end # ScoutRails
@@ -12,19 +12,13 @@ class ScoutRails::Layaway
12
12
 
13
13
  def deposit_and_deliver
14
14
  new_data = ScoutRails::Agent.instance.store.metric_hash
15
- controller_count = 0
16
- new_data.each do |meta,stats|
17
- if meta.metric_name =~ /\AController/
18
- controller_count += stats.call_count
19
- end
20
- end
21
- ScoutRails::Agent.instance.logger.debug "Depositing #{controller_count} requests into #{Time.at(slot).strftime("%m/%d/%y %H:%M:%S %z")} slot."
22
-
15
+ log_deposited_requests(new_data)
23
16
  to_deliver = {}
24
17
  file.read_and_write do |old_data|
25
18
  old_data ||= Hash.new
26
19
  # merge data
27
- # if the previous minute has ended, its time to send those metrics
20
+ # if (1) there's data in the file and (2) there isn't any data yet for the current minute, this means we've
21
+ # collected all metrics for the previous slots and we're ready to deliver.
28
22
  if old_data.any? and old_data[slot].nil?
29
23
  to_deliver = old_data
30
24
  old_data = Hash.new
@@ -34,16 +28,7 @@ class ScoutRails::Layaway
34
28
  ScoutRails::Agent.instance.logger.debug "There is no data in the layaway file to deliver."
35
29
  end
36
30
  old_data[slot]=ScoutRails::Agent.instance.store.merge_data_and_clear(old_data[slot] || Hash.new)
37
- ScoutRails::Agent.instance.logger.debug "Saving the following #{old_data.size} time slots locally:"
38
- old_data.each do |k,v|
39
- controller_count = 0
40
- new_data.each do |meta,stats|
41
- if meta.metric_name =~ /\AController/
42
- controller_count += stats.call_count
43
- end
44
- end
45
- ScoutRails::Agent.instance.logger.debug "#{Time.at(k).strftime("%m/%d/%y %H:%M:%S %z")} => #{controller_count} requests"
46
- end
31
+ log_saved_requests(old_data,new_data)
47
32
  old_data
48
33
  end
49
34
  to_deliver.any? ? validate_data(to_deliver) : {}
@@ -73,4 +58,27 @@ class ScoutRails::Layaway
73
58
  t -= t.sec
74
59
  t.to_i
75
60
  end
61
+
62
+ def log_deposited_requests(new_data)
63
+ controller_count = 0
64
+ new_data.each do |meta,stats|
65
+ if meta.metric_name =~ /\AController/
66
+ controller_count += stats.call_count
67
+ end
68
+ end
69
+ ScoutRails::Agent.instance.logger.debug "Depositing #{controller_count} requests into #{Time.at(slot).strftime("%m/%d/%y %H:%M:%S %z")} slot."
70
+ end
71
+
72
+ def log_saved_requests(old_data,new_data)
73
+ ScoutRails::Agent.instance.logger.debug "Saving the following #{old_data.size} time slots locally:"
74
+ old_data.each do |k,v|
75
+ controller_count = 0
76
+ new_data.each do |meta,stats|
77
+ if meta.metric_name =~ /\AController/
78
+ controller_count += stats.call_count
79
+ end
80
+ end
81
+ ScoutRails::Agent.instance.logger.debug "#{Time.at(k).strftime("%m/%d/%y %H:%M:%S %z")} => #{controller_count} requests"
82
+ end
83
+ end
76
84
  end
@@ -35,8 +35,10 @@ class ScoutRails::LayawayFile
35
35
  end
36
36
  end
37
37
  rescue Errno::ENOENT, Exception => e
38
- ScoutRails::Agent.instance.logger.error(e.message)
38
+ ScoutRails::Agent.instance.logger.error("Unable to access the layaway file [#{e.message}]. The user running the app must have read+write access.")
39
39
  ScoutRails::Agent.instance.logger.debug(e.backtrace.split("\n"))
40
+ # ensure the in-memory metric hash is cleared so data doesn't continue to accumulate.
41
+ ScoutRails::Agent.instance.store.metric_hash = {}
40
42
  end
41
43
 
42
44
  def get_data(f)
@@ -1,3 +1,3 @@
1
1
  module ScoutRails
2
- VERSION = "1.0.5.pre"
2
+ VERSION = "1.0.5"
3
3
  end
data/lib/scout_rails.rb CHANGED
@@ -5,6 +5,8 @@ require 'set'
5
5
  require 'net/http'
6
6
  require File.expand_path('../scout_rails/version.rb', __FILE__)
7
7
  require File.expand_path('../scout_rails/agent.rb', __FILE__)
8
+ require File.expand_path('../scout_rails/agent/logging.rb', __FILE__)
9
+ require File.expand_path('../scout_rails/agent/reporting.rb', __FILE__)
8
10
  require File.expand_path('../scout_rails/layaway.rb', __FILE__)
9
11
  require File.expand_path('../scout_rails/layaway_file.rb', __FILE__)
10
12
  require File.expand_path('../scout_rails/config.rb', __FILE__)
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.5.pre
5
- prerelease: 6
4
+ version: 1.0.5
5
+ prerelease:
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-10-16 00:00:00.000000000 Z
13
+ date: 2012-10-25 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.
@@ -28,6 +28,8 @@ files:
28
28
  - Rakefile
29
29
  - lib/scout_rails.rb
30
30
  - lib/scout_rails/agent.rb
31
+ - lib/scout_rails/agent/logging.rb
32
+ - lib/scout_rails/agent/reporting.rb
31
33
  - lib/scout_rails/config.rb
32
34
  - lib/scout_rails/environment.rb
33
35
  - lib/scout_rails/instruments/active_record_instruments.rb
@@ -64,9 +66,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
64
66
  required_rubygems_version: !ruby/object:Gem::Requirement
65
67
  none: false
66
68
  requirements:
67
- - - ! '>'
69
+ - - ! '>='
68
70
  - !ruby/object:Gem::Version
69
- version: 1.3.1
71
+ version: '0'
70
72
  requirements: []
71
73
  rubyforge_project: scout_rails
72
74
  rubygems_version: 1.8.10