scout_rails 1.0.5.pre → 1.0.5

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.
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