rackamole 0.0.9 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +1 -1
- data/lib/rackamole/alert/twitt.rb +12 -3
- data/lib/rackamole/mole.rb +84 -41
- data/lib/rackamole/stash/base.rb +25 -0
- data/lib/rackamole/stash/collector.rb +93 -0
- data/lib/rackamole/stash/fault.rb +10 -0
- data/lib/rackamole/stash/perf.rb +10 -0
- data/lib/rackamole/store/mongo_db.rb +11 -9
- data/lib/rackamole.rb +1 -1
- data/samples/rails/moled/app/controllers/fred_controller.rb +1 -0
- data/samples/rails/moled/config/environment.rb +8 -1
- data/samples/rails/moled/log/development.log +20 -0
- data/samples/rails/moled/log/production.log +10 -0
- data/samples/sinatra/moled.rb +34 -12
- data/samples/sinatra/public/.DS_Store +0 -0
- data/samples/sinatra/public/images/mole_logo.png +0 -0
- data/samples/sinatra/public/stylesheets/styles.css +58 -0
- data/samples/sinatra/views/index.erb +14 -0
- data/samples/sinatra/views/layout.erb +23 -0
- data/samples/sinatra/views/params.erb +1 -1
- data/samples/sinatra/views/post.erb +1 -0
- data/samples/sinatra/views/slow.erb +1 -0
- data/spec/rackamole/alert/twitt_spec.rb +21 -8
- data/spec/rackamole/mole_spec.rb +221 -46
- data/spec/rackamole/stash/collector_spec.rb +64 -0
- data/spec/rackamole/stash/fault_spec.rb +20 -0
- data/spec/rackamole/stash/perf_spec.rb +16 -0
- data/spec/rackamole/store/mongo_db_spec.rb +1 -0
- data/{lib/rackamole/config.rb → z_experiments/configuration.rb} +0 -0
- metadata +27 -4
- data/samples/sinatra/blee.rb +0 -7
data/Rakefile
CHANGED
@@ -28,6 +28,6 @@ PROJ.rcov.opts = ["--sort", "coverage", "-T", '-x mongo']
|
|
28
28
|
depend_on "logging" , ">= 1.2.2"
|
29
29
|
depend_on "hitimes" , ">= 1.0.3"
|
30
30
|
depend_on "mongo" , ">= 0.17.1"
|
31
|
-
|
31
|
+
depend_on "chronic" , ">= 0.2.3"
|
32
32
|
depend_on "twitter4r" , ">= 0.3.0"
|
33
33
|
depend_on "actionmailer" , ">= 2.1.0"
|
@@ -4,7 +4,13 @@ module Rackamole::Alert
|
|
4
4
|
# Leverage twitter as a notification client. You can setup a private twitter account
|
5
5
|
# and have your moled app twitt exception/perf alerts...
|
6
6
|
class Twitt
|
7
|
-
|
7
|
+
|
8
|
+
# Twitt an alert
|
9
|
+
def self.deliver_alert( username, password, attrs )
|
10
|
+
@twitt ||= Twitt.new( username, password )
|
11
|
+
@twitt.send_alert( attrs )
|
12
|
+
end
|
13
|
+
|
8
14
|
# This class is used to send out moled twitter notification. This feature is enabled
|
9
15
|
# by setting both :twitter_auth and twitt_on options on the Rack::Mole. When a moled
|
10
16
|
# feature comes around it will be twitted on your configured account. This allow your
|
@@ -41,8 +47,11 @@ module Rackamole::Alert
|
|
41
47
|
when Rackamole.perf : "[Perf] #{twitt_msg}\n#{format_time(args[:request_time])} secs"
|
42
48
|
when Rackamole.fault : "[Fault] #{twitt_msg}\n#{args[:fault]}"
|
43
49
|
else nil
|
44
|
-
end
|
45
|
-
|
50
|
+
end
|
51
|
+
if twitt_msg
|
52
|
+
twitt_msg += " - #{args[:created_at].strftime( "%H:%M:%S")}"
|
53
|
+
twitt.status( :post, truncate( twitt_msg ) )
|
54
|
+
end
|
46
55
|
twitt_msg
|
47
56
|
rescue => boom
|
48
57
|
$stderr.puts "TWITT mole failed with #{boom}"
|
data/lib/rackamole/mole.rb
CHANGED
@@ -17,10 +17,18 @@ module Rack
|
|
17
17
|
# === Options
|
18
18
|
#
|
19
19
|
# :app_name :: The name of the application (Default: Moled App)
|
20
|
+
# :log_level :: Rackamole logger level. (Default: info )
|
20
21
|
# :environment :: The environment for the application ie :environment => RAILS_ENV
|
21
22
|
# :perf_threshold :: Any request taking longer than this value will get moled. Default: 10secs
|
22
23
|
# :moleable :: Enable/Disable the MOle (Default:true)
|
23
24
|
# :store :: The storage instance ie log file or mongodb [Default:stdout]
|
25
|
+
# :expiration :: Number of seconds to expiration. The mole will not keep sending alert if a particular
|
26
|
+
# mole type has been reported in the past. This threshold specifies the limit at which
|
27
|
+
# the previously sent alerts will expire and thus will be sent again.
|
28
|
+
# For instance, it might be the case that the app is consistently slow for a particular action.
|
29
|
+
# On the first encounter an alert will be sent ( if configured ). Any subsequent requests for this action
|
30
|
+
# will not fire an alert until the expiration threshold is hit. The default is 1 hour.
|
31
|
+
# Setting this threshold to Rackamole::Stash::Collector::NEVER will result in alerts being fired continually.
|
24
32
|
# :user_key :: If sessions are enable, this represents the session key for the user name or
|
25
33
|
# user_id.
|
26
34
|
# ==
|
@@ -46,18 +54,22 @@ module Rack
|
|
46
54
|
# :email => { :from => 'fred@acme.com', :to => ['blee@acme.com', 'doh@acme.com'], :alert_on => [Rackamole.perf, Rackamole.fault] }
|
47
55
|
# ==
|
48
56
|
#
|
49
|
-
def initialize( app, opts={} )
|
50
|
-
@app
|
57
|
+
def initialize( app, opts={} )
|
58
|
+
@app = app
|
51
59
|
init_options( opts )
|
52
60
|
validate_options
|
61
|
+
@logger = Rackamole::Logger.new( :logger_name => 'RACKAMOLE', :log_level => options[:log_level] )
|
53
62
|
end
|
54
63
|
|
55
64
|
# Entering the MOle zone...
|
56
65
|
# Watches incoming requests and report usage information. The mole will also track request that
|
57
66
|
# are taking longer than expected and also report any requests that are raising exceptions.
|
58
|
-
def call( env )
|
67
|
+
def call( env )
|
59
68
|
# Bail if application is not moleable
|
60
69
|
return @app.call( env ) unless moleable?
|
70
|
+
|
71
|
+
@stash = env['mole.stash'] if env['mole.stash']
|
72
|
+
@stash = Rackamole::Stash::Collector.new( options[:app_name], options[:environment], options[:expiration] ) unless stash
|
61
73
|
|
62
74
|
status, headers, body = nil
|
63
75
|
elapsed = Hitimes::Interval.measure do
|
@@ -76,7 +88,7 @@ module Rack
|
|
76
88
|
# ===========================================================================
|
77
89
|
private
|
78
90
|
|
79
|
-
attr_reader :options #:nodoc:
|
91
|
+
attr_reader :options, :logger, :stash #:nodoc:
|
80
92
|
|
81
93
|
# Load up configuration options
|
82
94
|
def init_options( opts )
|
@@ -86,7 +98,9 @@ module Rack
|
|
86
98
|
# Mole default options
|
87
99
|
def default_options
|
88
100
|
{
|
89
|
-
:moleable => true,
|
101
|
+
:moleable => true,
|
102
|
+
:log_level => :info,
|
103
|
+
:expiration => 60*60, # 1 hour
|
90
104
|
:app_name => "Moled App",
|
91
105
|
:environment => 'test',
|
92
106
|
:excluded_paths => [/.?\.ico/, /.?\.png/],
|
@@ -109,24 +123,60 @@ module Rack
|
|
109
123
|
end
|
110
124
|
|
111
125
|
# Send moled info to store and potentially send out alerts...
|
112
|
-
def mole_feature( env, elapsed, status, headers, body )
|
113
|
-
|
126
|
+
def mole_feature( env, elapsed, status, headers, body )
|
127
|
+
env['mole.stash'] = stash
|
114
128
|
|
129
|
+
attrs = mole_info( env, elapsed, status, headers, body )
|
130
|
+
|
131
|
+
# If nothing to mole bail out!
|
132
|
+
return if attrs.empty?
|
133
|
+
|
115
134
|
# send info to configured store
|
116
135
|
options[:store].mole( attrs )
|
117
136
|
|
118
|
-
#
|
119
|
-
|
120
|
-
|
137
|
+
# Check for dups. If we've logged this req before don't log it again...
|
138
|
+
unless duplicated?( env, attrs )
|
139
|
+
# send email alert ?
|
140
|
+
if alertable?( :email, attrs[:type] )
|
141
|
+
logger.debug ">>> Sending out email on mole type #{attrs[:type]} to #{options[:email][:to].join( ", ")}"
|
142
|
+
Rackamole::Alert::Emole.deliver_alert( options[:email][:from], options[:email][:to], attrs )
|
143
|
+
end
|
144
|
+
|
145
|
+
# send twitter alert ?
|
146
|
+
if alertable?( :twitter, attrs[:type] )
|
147
|
+
logger.debug ">>> Sending out twitt on mole type #{attrs[:type]} on @#{options[:twitter][:username]}"
|
148
|
+
Rackamole::Alert::Twitt.deliver_alert( options[:twitter][:username], options[:twitter][:password], attrs )
|
149
|
+
end
|
150
|
+
end
|
151
|
+
rescue => boom
|
152
|
+
logger.error "!! MOLE RECORDING CRAPPED OUT !! -- #{boom}"
|
153
|
+
boom.backtrace.each { |l| logger.error l }
|
154
|
+
end
|
155
|
+
|
156
|
+
# Check if we've already seen such an error
|
157
|
+
def duplicated?( env, attrs )
|
158
|
+
# Skip features for now...
|
159
|
+
return true if attrs[:type] == Rackamole.feature
|
160
|
+
|
161
|
+
# Don't bother if expiration is set to never. ie fire alerts all the time
|
162
|
+
return false if options[:expiration] == Rackamole::Stash::Collector::NEVER
|
163
|
+
|
164
|
+
now = Time.now
|
165
|
+
app_id = [attrs[:app_name], attrs[:environment]].join( '_' )
|
166
|
+
path = attrs[:route_info] ? "#{attrs[:route_info][:controller]}#{attrs[:route_info][:action]}" : attrs[:path]
|
167
|
+
|
168
|
+
# Check expired entries
|
169
|
+
stash.expire!
|
170
|
+
|
171
|
+
# check if we've seen this error before. If so stash it.
|
172
|
+
if attrs[:type] == Rackamole.fault
|
173
|
+
return stash.stash_fault( path, attrs[:stack].first, now.utc )
|
121
174
|
end
|
122
175
|
|
123
|
-
#
|
124
|
-
if
|
125
|
-
|
176
|
+
# Check if we've seen this perf issue before. If so stash it
|
177
|
+
if attrs[:type] == Rackamole.perf
|
178
|
+
return stash.stash_perf( path, attrs[:request_time], now.utc )
|
126
179
|
end
|
127
|
-
rescue => boom
|
128
|
-
$stderr.puts "!! MOLE RECORDING CRAPPED OUT !! -- #{boom}"
|
129
|
-
boom.backtrace.each { |l| $stderr.puts l }
|
130
180
|
end
|
131
181
|
|
132
182
|
# Check if an options is set and configured
|
@@ -148,12 +198,7 @@ module Rack
|
|
148
198
|
return false unless options[filter][:alert_on]
|
149
199
|
options[filter][:alert_on].include?( type )
|
150
200
|
end
|
151
|
-
|
152
|
-
# Create or retrieve twitter client
|
153
|
-
def twitt
|
154
|
-
@twitt ||= Rackamole::Alert::Twitt.new( options[:twitter][:username], options[:twitter][:password] )
|
155
|
-
end
|
156
|
-
|
201
|
+
|
157
202
|
# Check if this request should be moled according to the exclude filters
|
158
203
|
def mole_request?( request )
|
159
204
|
options[:excluded_paths].each do |exclude_path|
|
@@ -166,9 +211,7 @@ module Rack
|
|
166
211
|
def mole_info( env, elapsed, status, headers, body )
|
167
212
|
request = Rack::Request.new( env )
|
168
213
|
info = OrderedHash.new
|
169
|
-
|
170
|
-
# dump( env )
|
171
|
-
|
214
|
+
|
172
215
|
return info unless mole_request?( request )
|
173
216
|
|
174
217
|
session = env['rack.session']
|
@@ -206,6 +249,7 @@ module Rack
|
|
206
249
|
info[:method] = env['REQUEST_METHOD']
|
207
250
|
info[:path] = request.path
|
208
251
|
info[:route_info] = route if route
|
252
|
+
info[:created_at] = Time.now.utc
|
209
253
|
|
210
254
|
# Dump request params
|
211
255
|
unless request.params.empty?
|
@@ -261,28 +305,27 @@ module Rack
|
|
261
305
|
# Fetch route info if any...
|
262
306
|
def get_route( request )
|
263
307
|
return nil unless defined?( RAILS_ENV )
|
264
|
-
|
265
308
|
# Check for invalid route exception...
|
266
309
|
begin
|
267
310
|
return ::ActionController::Routing::Routes.recognize_path( request.path, {:method => request.request_method.downcase.to_sym } )
|
268
|
-
rescue
|
311
|
+
rescue => boom
|
269
312
|
return nil
|
270
313
|
end
|
271
314
|
end
|
272
315
|
|
273
|
-
# Dump env to stdout
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
316
|
+
# Debug - Dump env to stdout
|
317
|
+
def dump( env, level=0 )
|
318
|
+
env.keys.sort{ |a,b| a.to_s <=> b.to_s }.each do |k|
|
319
|
+
value = env[k]
|
320
|
+
if value.respond_to?(:each_pair)
|
321
|
+
puts "%s %-#{40-level}s" % [' '*level,k]
|
322
|
+
dump( env[k], level+1 )
|
323
|
+
elsif value.instance_of?(::ActionController::Request) or value.instance_of?(::ActionController::Response)
|
324
|
+
puts "%s %-#{40-level}s %s" % [ ' '*level, k, value.class ]
|
325
|
+
else
|
326
|
+
puts "%s %-#{40-level}s %s" % [ ' '*level, k, value.inspect ]
|
327
|
+
end
|
328
|
+
end
|
329
|
+
end
|
287
330
|
end
|
288
331
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Rackamole::Stash
|
2
|
+
# Stash mole information into the env. These objects are meant to track
|
3
|
+
# instances of a similar event occurring in the application so that alerts
|
4
|
+
# are kept under control when shit hits the fan...
|
5
|
+
class Base
|
6
|
+
attr_reader :path, :timestamp, :count
|
7
|
+
|
8
|
+
# =======================================================================--
|
9
|
+
protected
|
10
|
+
|
11
|
+
def initialize( path, timestamp )
|
12
|
+
@path = path
|
13
|
+
@count = 1
|
14
|
+
@timestamp = timestamp
|
15
|
+
end
|
16
|
+
|
17
|
+
public
|
18
|
+
|
19
|
+
# Update count and timestamp
|
20
|
+
def update( timestamp )
|
21
|
+
@timestamp = timestamp
|
22
|
+
@count += 1
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
module Rackamole::Stash
|
2
|
+
# Caches perfs and faults. If either has been seen before update their
|
3
|
+
# respective counts. This is used by the mole to track if a perf or exception
|
4
|
+
# was previously recorded.
|
5
|
+
class Collector
|
6
|
+
|
7
|
+
attr_reader :app_id
|
8
|
+
|
9
|
+
NEVER = -1
|
10
|
+
|
11
|
+
def initialize( app_name, environment, expiration=24*60*60 )
|
12
|
+
@expiration = expiration
|
13
|
+
@app_id = app_name + "_" + environment.to_s
|
14
|
+
@faults = {}
|
15
|
+
@perfs = {}
|
16
|
+
end
|
17
|
+
|
18
|
+
# Remove all entries that have expired
|
19
|
+
def expire!
|
20
|
+
expire_faults!
|
21
|
+
expire_perfs!
|
22
|
+
end
|
23
|
+
|
24
|
+
# Delete all faults older than expiration
|
25
|
+
def expire_faults!
|
26
|
+
now = Time.now
|
27
|
+
faults.each_pair do |stack, fault|
|
28
|
+
if (now - fault.timestamp) >= expiration
|
29
|
+
faults.delete( stack )
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Delete all perfs older than expiration
|
35
|
+
def expire_perfs!
|
36
|
+
now = Time.now.utc
|
37
|
+
perfs.each_pair do |path, perf|
|
38
|
+
if (now - perf.timestamp) >= expiration
|
39
|
+
perfs.delete( path )
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Log or update fault if found...
|
45
|
+
# Returns true if updated or false if created
|
46
|
+
def stash_fault( path, stack, timestamp )
|
47
|
+
fault = find_fault( path, stack )
|
48
|
+
if fault
|
49
|
+
fault.update( timestamp )
|
50
|
+
return true
|
51
|
+
end
|
52
|
+
fault = create_fault( path, stack, timestamp )
|
53
|
+
faults[stack] = fault
|
54
|
+
false
|
55
|
+
end
|
56
|
+
|
57
|
+
# Log or update performance issue if found...
|
58
|
+
# Returns true if updated or false if created
|
59
|
+
def stash_perf( path, elapsed, timestamp )
|
60
|
+
perf = find_perf( path )
|
61
|
+
if perf
|
62
|
+
perf.update( timestamp )
|
63
|
+
return true
|
64
|
+
end
|
65
|
+
perf = create_perf( path, elapsed, timestamp )
|
66
|
+
perfs[path] = perf
|
67
|
+
false
|
68
|
+
end
|
69
|
+
|
70
|
+
# =========================================================================
|
71
|
+
private
|
72
|
+
|
73
|
+
attr_reader :faults, :perfs, :expiration
|
74
|
+
|
75
|
+
def create_fault( path, stack, timestamp )
|
76
|
+
faults[stack] = Rackamole::Stash::Fault.new( path, stack, timestamp )
|
77
|
+
end
|
78
|
+
|
79
|
+
def create_perf( path, elapsed, timestamp )
|
80
|
+
perfs[path] = Rackamole::Stash::Perf.new( path, elapsed, timestamp )
|
81
|
+
end
|
82
|
+
|
83
|
+
# Check if we've seen a similar fault on this application
|
84
|
+
def find_fault( path, stack )
|
85
|
+
faults[stack]
|
86
|
+
end
|
87
|
+
|
88
|
+
# Check if we've seen this perf issue on this application
|
89
|
+
def find_perf( path )
|
90
|
+
perfs[path]
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -96,21 +96,22 @@ module Rackamole
|
|
96
96
|
row[min_field(:context)] = args.delete( :path )
|
97
97
|
end
|
98
98
|
|
99
|
-
feature = features.find_one( row )
|
100
|
-
return feature if feature
|
101
|
-
|
102
|
-
|
103
|
-
|
99
|
+
feature = features.find_one( row, :fields => ['_id'] )
|
100
|
+
return feature['_id'] if feature
|
101
|
+
|
102
|
+
row[min_field(:created_at)] = args[:created_at]
|
103
|
+
|
104
|
+
features.save( row )
|
104
105
|
end
|
105
106
|
|
106
107
|
# Insert a new feature in the db
|
107
108
|
# NOTE : Using min key to reduce storage needs. I know not that great for higher level api's :-(
|
108
109
|
# also saving date and time as ints. same deal...
|
109
|
-
def save_log(
|
110
|
-
now =
|
110
|
+
def save_log( feature_id, args )
|
111
|
+
now = args.delete( :created_at )
|
111
112
|
row = {
|
112
113
|
min_field( :type ) => args[:type],
|
113
|
-
min_field( :feature_id ) =>
|
114
|
+
min_field( :feature_id ) => feature_id.to_s,
|
114
115
|
min_field( :date_id ) => ("%4d%02d%02d" %[now.year, now.month, now.day]).to_i,
|
115
116
|
min_field( :time_id ) => ("%02d%02d%02d" %[now.hour, now.min, now.sec] ).to_i
|
116
117
|
}
|
@@ -151,7 +152,8 @@ module Rackamole
|
|
151
152
|
:params => :par,
|
152
153
|
:ruby_version => :ver,
|
153
154
|
:fault => :msg,
|
154
|
-
:stack => :sta
|
155
|
+
:stack => :sta,
|
156
|
+
:created_at => :cro
|
155
157
|
}
|
156
158
|
end
|
157
159
|
end
|
data/lib/rackamole.rb
CHANGED
@@ -9,7 +9,14 @@ require File.join(File.dirname(__FILE__), 'boot')
|
|
9
9
|
Rails::Initializer.run do |config|
|
10
10
|
|
11
11
|
require 'rackamole'
|
12
|
-
config.middleware.use Rack::Mole, {
|
12
|
+
config.middleware.use Rack::Mole, {
|
13
|
+
:app_name => "Moled Rails",
|
14
|
+
:user_key => :user_name,
|
15
|
+
:expiration => 60,
|
16
|
+
:perf_threshold => 0.2,
|
17
|
+
:twitter => { :username => 'moled', :password => 'fernand~1', :alert_on => [Rackamole.perf, Rackamole.fault] },
|
18
|
+
:user_key => :user_name
|
19
|
+
}
|
13
20
|
|
14
21
|
# Settings in config/environments/* take precedence over those specified here.
|
15
22
|
# Application configuration should go into files in config/initializers
|
@@ -78,3 +78,23 @@ Processing FredController#show (for 127.0.0.1 at 2009-11-15 11:54:08) [GET]
|
|
78
78
|
Parameters: {"id"=>"10"}
|
79
79
|
Rendering fred/show
|
80
80
|
Completed in 7ms (View: 6, DB: 0) | 200 OK [http://localhost/fred/10]
|
81
|
+
|
82
|
+
|
83
|
+
Processing FredController#index (for 127.0.0.1 at 2009-11-28 13:08:32) [GET]
|
84
|
+
Rendering fred/index
|
85
|
+
Completed in 135ms (View: 11, DB: 0) | 200 OK [http://localhost/fred]
|
86
|
+
|
87
|
+
|
88
|
+
Processing FredController#index (for 127.0.0.1 at 2009-11-28 13:08:40) [GET]
|
89
|
+
Rendering fred/index
|
90
|
+
Completed in 2ms (View: 0, DB: 0) | 200 OK [http://localhost/fred]
|
91
|
+
|
92
|
+
|
93
|
+
Processing FredController#index (for 127.0.0.1 at 2009-11-28 13:09:19) [GET]
|
94
|
+
Rendering fred/index
|
95
|
+
Completed in 1004ms (View: 2, DB: 0) | 200 OK [http://localhost/fred]
|
96
|
+
|
97
|
+
|
98
|
+
Processing FredController#index (for 127.0.0.1 at 2009-11-28 13:09:23) [GET]
|
99
|
+
Rendering fred/index
|
100
|
+
Completed in 1002ms (View: 1, DB: 0) | 200 OK [http://localhost/fred]
|
@@ -34,3 +34,13 @@ RuntimeError (Oh snap!):
|
|
34
34
|
app/controllers/fred_controller.rb:7:in `edit'
|
35
35
|
|
36
36
|
Rendering /Users/fgaliana/work/git/rackamole/samples/rails/moled/public/500.html (500 Internal Server Error)
|
37
|
+
|
38
|
+
|
39
|
+
Processing FredController#index (for 127.0.0.1 at 2009-11-28 13:10:07) [GET]
|
40
|
+
Rendering fred/index
|
41
|
+
Completed in 1003ms (View: 1, DB: 0) | 200 OK [http://localhost/fred]
|
42
|
+
|
43
|
+
|
44
|
+
Processing FredController#index (for 127.0.0.1 at 2009-11-28 13:10:11) [GET]
|
45
|
+
Rendering fred/index
|
46
|
+
Completed in 1001ms (View: 0, DB: 0) | 200 OK [http://localhost/fred]
|
data/samples/sinatra/moled.rb
CHANGED
@@ -2,34 +2,56 @@ require 'rubygems'
|
|
2
2
|
require 'sinatra'
|
3
3
|
require 'rackamole'
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
use Rack::
|
5
|
+
set :environment, :production
|
6
|
+
|
7
|
+
configure :production do
|
8
|
+
set :sessions, true
|
9
|
+
use Rack::Lint
|
10
|
+
use Rack::Session::Cookie
|
11
|
+
use Rack::Mole, {
|
12
|
+
:app_name => "Moled Franky",
|
13
|
+
:log_level => :debug,
|
14
|
+
:user_key => :user_name,
|
15
|
+
:expiration => 10,
|
16
|
+
:perf_threshold => 0.2,
|
17
|
+
:twitter => { :username => 'moled', :password => 'fernand~1', :alert_on => [Rackamole.perf, Rackamole.fault] },
|
18
|
+
:excluded_paths => [ /.+?\.css/, /.+?\.png/ ]
|
19
|
+
}
|
10
20
|
end
|
11
21
|
|
12
22
|
before do
|
13
23
|
session[:user_name] = "Fernand"
|
14
24
|
end
|
15
25
|
|
26
|
+
get '/' do
|
27
|
+
puts "Yo"
|
28
|
+
erb :index
|
29
|
+
end
|
30
|
+
|
16
31
|
get '/normal' do
|
17
|
-
@blee
|
18
|
-
session[:fred] = "
|
32
|
+
@blee = "Hello World!"
|
33
|
+
session[:fred] = "oh dear"
|
34
|
+
|
19
35
|
erb :normal
|
20
36
|
end
|
21
37
|
|
38
|
+
post '/post' do
|
39
|
+
@post = params[:blee]
|
40
|
+
erb :post
|
41
|
+
end
|
42
|
+
|
22
43
|
get '/params/:id' do
|
23
|
-
@blee
|
24
|
-
session[:
|
44
|
+
@blee = params[:id]
|
45
|
+
session[:blee] = "something interesting"
|
46
|
+
|
25
47
|
erb :params
|
26
48
|
end
|
27
49
|
|
28
50
|
get '/error' do
|
29
|
-
raise "Oh
|
51
|
+
raise "Oh Snap!"
|
30
52
|
end
|
31
53
|
|
32
54
|
get '/slow' do
|
33
|
-
sleep
|
34
|
-
|
55
|
+
sleep 0.2
|
56
|
+
erb :slow
|
35
57
|
end
|
Binary file
|
Binary file
|
@@ -0,0 +1,58 @@
|
|
1
|
+
* {
|
2
|
+
outline-color: invert;
|
3
|
+
outline-style: none;
|
4
|
+
outline-width: medium;
|
5
|
+
margin: 0px;
|
6
|
+
padding: 0px;
|
7
|
+
}
|
8
|
+
|
9
|
+
body {
|
10
|
+
font-family: "Trebuchet","Lucida Grande","Lucida Sans Unicode","bitstream vera sans","trebuchet ms","verdana";
|
11
|
+
background: #f4f4f4 repeat-x top left;
|
12
|
+
color: #ffffff;
|
13
|
+
font-size: 2em;
|
14
|
+
font-size-adjust: none;
|
15
|
+
font-stretch: normal;
|
16
|
+
font-style: normal;
|
17
|
+
font-variant: normal;
|
18
|
+
font-weight: normal;
|
19
|
+
margin: 0;
|
20
|
+
}
|
21
|
+
|
22
|
+
#overall {
|
23
|
+
margin: 0px auto;
|
24
|
+
width: 1000px;
|
25
|
+
overflow: none;
|
26
|
+
margin-top: 30px;
|
27
|
+
}
|
28
|
+
|
29
|
+
#logo {
|
30
|
+
float:left;
|
31
|
+
width:45%;
|
32
|
+
margin-bottom:10px;
|
33
|
+
}
|
34
|
+
|
35
|
+
#main {
|
36
|
+
clear: left;
|
37
|
+
margin: 20px 10px 10px 20px;
|
38
|
+
overflow: none;
|
39
|
+
}
|
40
|
+
|
41
|
+
h1 {
|
42
|
+
font-size: 2.2em;
|
43
|
+
color: #434343;
|
44
|
+
}
|
45
|
+
|
46
|
+
a {
|
47
|
+
color: #cccccc;
|
48
|
+
text-decoration: none;
|
49
|
+
}
|
50
|
+
|
51
|
+
a:hover {
|
52
|
+
text_decoration: underline;
|
53
|
+
}
|
54
|
+
|
55
|
+
input {
|
56
|
+
font-size: 1em;
|
57
|
+
color: #cccccc;
|
58
|
+
}
|
@@ -0,0 +1,14 @@
|
|
1
|
+
<h1>Mole sampler</h1>
|
2
|
+
|
3
|
+
<ul>
|
4
|
+
<li><a href="/normal">Plain ol' get</a></li>
|
5
|
+
<li><a href="/params/10">Get with params</a></li>
|
6
|
+
<li><a href="/error">Uncaught fault</a></li>
|
7
|
+
<li><a href="/slow">Slow request</a></li>
|
8
|
+
<li>
|
9
|
+
<form action="/post" method="post" >
|
10
|
+
<input type="text" name="blee"/>
|
11
|
+
<input type="submit" value="push me"/>
|
12
|
+
</form>
|
13
|
+
</li>
|
14
|
+
</ul>
|
@@ -0,0 +1,23 @@
|
|
1
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
2
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
3
|
+
<head>
|
4
|
+
<title>R A C K A M O L E - Sampler</title>
|
5
|
+
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
6
|
+
<link rel="icon" href="/favicon.ico" type="image/x-icon"/>
|
7
|
+
<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon"/>
|
8
|
+
<link rel="stylesheet" href="/stylesheets/styles.css" type="text/css" media="screen" />
|
9
|
+
</head>
|
10
|
+
|
11
|
+
<body id="body">
|
12
|
+
<div id="overall">
|
13
|
+
<div id="logo">
|
14
|
+
<a href="/">
|
15
|
+
<img src='/images/mole_logo.png' style="border:none"/>
|
16
|
+
</a>
|
17
|
+
</div>
|
18
|
+
<div id="main">
|
19
|
+
<%= yield %>
|
20
|
+
</div>
|
21
|
+
</div>
|
22
|
+
</body>
|
23
|
+
</html>
|
@@ -1 +1 @@
|
|
1
|
-
<h1>Params -- <%=@
|
1
|
+
<h1>Params -- <%=@post%></h1>
|
@@ -0,0 +1 @@
|
|
1
|
+
<h1>Posted - <%=@post%></h1>
|
@@ -0,0 +1 @@
|
|
1
|
+
<h1>I am the slow one</h1>
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require File.join(File.dirname(__FILE__), %w[.. .. spec_helper])
|
2
|
+
require 'chronic'
|
2
3
|
|
3
4
|
describe Rackamole::Alert::Twitt do
|
4
5
|
before( :each ) do
|
@@ -22,11 +23,23 @@ describe Rackamole::Alert::Twitt do
|
|
22
23
|
describe '#send_alert' do
|
23
24
|
before( :each ) do
|
24
25
|
@args = OrderedHash.new
|
25
|
-
@args[:type]
|
26
|
-
@args[:app_name]
|
27
|
-
@args[:host]
|
28
|
-
@args[:user_name]
|
29
|
-
@args[:path]
|
26
|
+
@args[:type] = Rackamole.feature
|
27
|
+
@args[:app_name] = 'Test'
|
28
|
+
@args[:host] = 'Fred'
|
29
|
+
@args[:user_name] = 'Fernand'
|
30
|
+
@args[:path] = '/blee/fred'
|
31
|
+
@args[:created_at] = Chronic.parse( "2009/11/19" )
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should twitt a feature alert using class method correctly" do
|
35
|
+
twitt = mock( Rackamole::Alert::Twitt )
|
36
|
+
client = Twitter::Client.stub!( :new )
|
37
|
+
|
38
|
+
Rackamole::Alert::Twitt.should_receive( :new ).with( 'blee', 'duh').once.and_return( twitt )
|
39
|
+
# client.should_receive( :new ).once.and_return( client )
|
40
|
+
twitt.should_receive( :send_alert ).with( @args ).once.and_return( "yeah" )
|
41
|
+
|
42
|
+
Rackamole::Alert::Twitt.deliver_alert( "blee", "duh", @args )
|
30
43
|
end
|
31
44
|
|
32
45
|
it "should twitt a feature alert correctly" do
|
@@ -36,7 +49,7 @@ describe Rackamole::Alert::Twitt do
|
|
36
49
|
# client.should_receive( :new ).exactly(1).with( 'fernand', 'blee' )
|
37
50
|
client.should_receive( :status ).once
|
38
51
|
|
39
|
-
@alert.send_alert( @args ).should == "[Feature] Test on Fred - Fernand\n/blee/fred"
|
52
|
+
@alert.send_alert( @args ).should == "[Feature] Test on Fred - Fernand\n/blee/fred - 12:00:00"
|
40
53
|
end
|
41
54
|
|
42
55
|
it "should twitt a perf alert correctly" do
|
@@ -48,7 +61,7 @@ describe Rackamole::Alert::Twitt do
|
|
48
61
|
@alert.should_receive( :twitt ).once.and_return( client )
|
49
62
|
client.should_receive( :status ).once
|
50
63
|
|
51
|
-
@alert.send_alert( @args ).should == "[Perf] Test on Fred - Fernand\n/blee/fred\n10.0 secs"
|
64
|
+
@alert.send_alert( @args ).should == "[Perf] Test on Fred - Fernand\n/blee/fred\n10.0 secs - 12:00:00"
|
52
65
|
end
|
53
66
|
|
54
67
|
it "should twitt a perf alert correctly" do
|
@@ -60,7 +73,7 @@ describe Rackamole::Alert::Twitt do
|
|
60
73
|
@alert.should_receive( :twitt ).once.and_return( client )
|
61
74
|
client.should_receive( :status ).once
|
62
75
|
|
63
|
-
@alert.send_alert( @args ).should == "[Fault] Test on Fred - Fernand\n/blee/fred\nOh snap!"
|
76
|
+
@alert.send_alert( @args ).should == "[Fault] Test on Fred - Fernand\n/blee/fred\nOh snap! - 12:00:00"
|
64
77
|
end
|
65
78
|
end
|
66
79
|
|
data/spec/rackamole/mole_spec.rb
CHANGED
@@ -4,8 +4,21 @@ describe Rack::Mole do
|
|
4
4
|
include Rack::Test::Methods
|
5
5
|
|
6
6
|
before :each do
|
7
|
-
@response
|
8
|
-
@
|
7
|
+
@response = [ 200, {"Content-Type" => "text/plain"}, ["success"] ]
|
8
|
+
@test_store = TestStore.new
|
9
|
+
@test_env = {
|
10
|
+
'rack.session' => { :user_id => 100, :username => "fernand" },
|
11
|
+
'HTTP_X_FORWARDED_FOR' => '1.1.1.1',
|
12
|
+
'HTTP_USER_AGENT' => "Firefox"
|
13
|
+
}
|
14
|
+
@opts = {
|
15
|
+
:app_name => "Test App",
|
16
|
+
:environment => :test,
|
17
|
+
:excluded_paths => ['/should_bail'],
|
18
|
+
:perf_threshold => 0.1,
|
19
|
+
:user_key => :username,
|
20
|
+
:store => @test_store
|
21
|
+
}
|
9
22
|
end
|
10
23
|
|
11
24
|
class TestStore
|
@@ -32,58 +45,158 @@ describe Rack::Mole do
|
|
32
45
|
end
|
33
46
|
end
|
34
47
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
48
|
+
def slow_app( opts={} )
|
49
|
+
response = @response
|
50
|
+
@app ||= Rack::Builder.new do
|
51
|
+
use Rack::Lint
|
52
|
+
use Rack::Mole, opts
|
53
|
+
run lambda { |env| sleep(0.2); response }
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# ---------------------------------------------------------------------------
|
58
|
+
describe "fault duplicate" do
|
59
|
+
before( :each ) do
|
60
|
+
error_app( @opts )
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should mole a fault issue correctly" do
|
64
|
+
begin
|
65
|
+
get "/", nil, @test_env
|
66
|
+
rescue
|
67
|
+
last_request.env['mole.stash'].should_not be_nil
|
68
|
+
fault = last_request.env['mole.stash'].send( :find_fault, "/", "./spec/rackamole/mole_spec.rb:44:in `error_app'" )
|
69
|
+
fault.should_not be_nil
|
70
|
+
fault.count.should == 1
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should trap a recuring fault on given path correctly" do
|
75
|
+
env = @test_env
|
76
|
+
2.times do |i|
|
77
|
+
begin
|
78
|
+
get "/", nil, env
|
79
|
+
rescue
|
80
|
+
last_request.env['mole.stash'].should_not be_nil
|
81
|
+
fault = last_request.env['mole.stash'].send( :find_fault, "/", "./spec/rackamole/mole_spec.rb:44:in `error_app'" )
|
82
|
+
fault.should_not be_nil
|
83
|
+
fault.count.should == i+1
|
84
|
+
env = last_request.env
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should trap a recuring fault on different path correctly" do
|
90
|
+
env = @test_env
|
91
|
+
2.times do |i|
|
92
|
+
begin
|
93
|
+
env['PATH_INFO'] = "/#{i}"
|
94
|
+
get "/#{i}", nil, env
|
95
|
+
rescue
|
96
|
+
last_request.env['mole.stash'].should_not be_nil
|
97
|
+
fault = last_request.env['mole.stash'].send( :find_fault, "/", "./spec/rackamole/mole_spec.rb:44:in `error_app'" )
|
98
|
+
fault.should_not be_nil
|
99
|
+
fault.count.should == i+1
|
100
|
+
env = last_request.env
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# ---------------------------------------------------------------------------
|
107
|
+
describe "performance duplicate" do
|
108
|
+
before( :each ) do
|
109
|
+
@test_store = TestStore.new
|
110
|
+
slow_app( @opts )
|
111
|
+
end
|
43
112
|
|
113
|
+
it "should mole a perf issue correctly" do
|
114
|
+
get "/", nil, @test_env
|
115
|
+
last_request.env['mole.stash'].should_not be_nil
|
116
|
+
perf = last_request.env['mole.stash'].send( :find_perf, "/" )
|
117
|
+
perf.should_not be_nil
|
118
|
+
perf.count.should == 1
|
119
|
+
end
|
120
|
+
|
121
|
+
it "should trap a recuring perf on given path correctly" do
|
122
|
+
env = @test_env
|
123
|
+
2.times do |i|
|
124
|
+
get "/", nil, env
|
125
|
+
perf = last_request.env['mole.stash'].send( :find_perf, "/" )
|
126
|
+
perf.should_not be_nil
|
127
|
+
perf.count.should == i+1
|
128
|
+
env = last_request.env
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
it "should trap a recuring perf on different path correctly" do
|
133
|
+
env = @test_env
|
134
|
+
2.times do |i|
|
135
|
+
env['PATH_INFO'] = "/#{i}"
|
136
|
+
get "/#{i}", nil, env
|
137
|
+
last_request.env['mole.stash'].should_not be_nil
|
138
|
+
count = 0
|
139
|
+
while count <= i
|
140
|
+
perf = last_request.env['mole.stash'].send( :find_perf, "/#{count}" )
|
141
|
+
perf.should_not be_nil
|
142
|
+
perf.count.should == 1
|
143
|
+
count += 1
|
144
|
+
end
|
145
|
+
env = last_request.env
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
# ---------------------------------------------------------------------------
|
151
|
+
it "should mole a framwework exception correctly" do
|
152
|
+
error_app( @opts )
|
44
153
|
begin
|
45
154
|
get "/", nil, @test_env
|
46
155
|
rescue
|
47
|
-
@test_store.mole_result[:stack].should have(4).items
|
156
|
+
@test_store.mole_result[:stack].should have(4).items
|
157
|
+
last_request.env['mole.stash'].should_not be_nil
|
158
|
+
fault = last_request.env['mole.stash'].send( :find_fault, "/", "./spec/rackamole/mole_spec.rb:44:in `error_app'" )
|
159
|
+
fault.should_not be_nil
|
160
|
+
fault.count.should == 1
|
48
161
|
end
|
49
162
|
end
|
50
163
|
|
164
|
+
# ---------------------------------------------------------------------------
|
51
165
|
describe 'moling a request' do
|
52
166
|
before :each do
|
53
|
-
@
|
54
|
-
app(
|
55
|
-
:app_name => "Test App",
|
56
|
-
:environment => :test,
|
57
|
-
:perf_threshold => 0.1,
|
58
|
-
:user_key => { :session_key => :user_id, :extractor => lambda{ |k| "Fernand (#{k})"} },
|
59
|
-
:store => @test_store )
|
167
|
+
app( @opts )
|
60
168
|
end
|
61
169
|
|
62
170
|
it "should set the mole meta correctly" do
|
63
171
|
get "/fred/blee", nil, @test_env
|
64
|
-
@test_store.mole_result[:app_name].should
|
65
|
-
@test_store.mole_result[:environment].should
|
66
|
-
@test_store.mole_result[:user_id].should
|
67
|
-
@test_store.mole_result[:user_name].should
|
68
|
-
@test_store.mole_result[:ip].should
|
69
|
-
@test_store.mole_result[:browser].should
|
70
|
-
@test_store.mole_result[:method].should
|
71
|
-
@test_store.mole_result[:url].should
|
72
|
-
@test_store.mole_result[:path].should
|
73
|
-
@test_store.mole_result[:type].should
|
74
|
-
@test_store.mole_result[:params].should
|
75
|
-
@test_store.mole_result[:session].should_not
|
76
|
-
@test_store.mole_result[:session].should
|
172
|
+
@test_store.mole_result[:app_name].should == "Test App"
|
173
|
+
@test_store.mole_result[:environment].should == :test
|
174
|
+
@test_store.mole_result[:user_id].should be_nil
|
175
|
+
@test_store.mole_result[:user_name].should == 'fernand'
|
176
|
+
@test_store.mole_result[:ip].should == '1.1.1.1'
|
177
|
+
@test_store.mole_result[:browser].should == 'Firefox'
|
178
|
+
@test_store.mole_result[:method].should == 'GET'
|
179
|
+
@test_store.mole_result[:url].should == 'http://example.org/fred/blee'
|
180
|
+
@test_store.mole_result[:path].should == '/fred/blee'
|
181
|
+
@test_store.mole_result[:type].should == Rackamole.feature
|
182
|
+
@test_store.mole_result[:params].should be_nil
|
183
|
+
@test_store.mole_result[:session].should_not be_nil
|
184
|
+
@test_store.mole_result[:session].keys.should have(2).items
|
77
185
|
end
|
78
186
|
|
79
187
|
it "mole an exception correctly" do
|
80
188
|
begin
|
81
189
|
raise 'Oh snap!'
|
82
190
|
rescue => boom
|
83
|
-
|
191
|
+
@test_env['mole.exception'] = boom
|
192
|
+
get "/crap/out", nil, @test_env
|
84
193
|
@test_store.mole_result[:type].should == Rackamole.fault
|
85
194
|
@test_store.mole_result[:stack].should have(4).items
|
86
|
-
@test_store.mole_result[:fault].should == 'Oh snap!'
|
195
|
+
@test_store.mole_result[:fault].should == 'Oh snap!'
|
196
|
+
last_request.env['mole.stash'].should_not be_nil
|
197
|
+
fault = last_request.env['mole.stash'].send( :find_fault, "/", "./spec/rackamole/mole_spec.rb:189" )
|
198
|
+
fault.should_not be_nil
|
199
|
+
fault.count.should == 1
|
87
200
|
end
|
88
201
|
end
|
89
202
|
|
@@ -91,26 +204,86 @@ describe Rack::Mole do
|
|
91
204
|
get "/", { :blee => 'duh' }, @test_env
|
92
205
|
@test_store.mole_result[:params].should == { :blee => "duh".to_json }
|
93
206
|
end
|
207
|
+
|
208
|
+
it "should not mole an exclusion" do
|
209
|
+
get '/should_bail', nil, @test_env
|
210
|
+
@test_store.mole_result.should be_nil
|
211
|
+
end
|
94
212
|
end
|
95
213
|
|
214
|
+
# ---------------------------------------------------------------------------
|
96
215
|
describe 'username in session' do
|
97
|
-
before :each do
|
98
|
-
@test_store = TestStore.new
|
99
|
-
app(
|
100
|
-
:app_name => "Test App",
|
101
|
-
:environment => :test,
|
102
|
-
:perf_threshold => 0.1,
|
103
|
-
:user_key => :user_name,
|
104
|
-
:store => @test_store )
|
105
|
-
end
|
106
|
-
|
107
216
|
it "should pickup the user name from the session correctly" do
|
108
|
-
|
217
|
+
app( @opts )
|
218
|
+
get "/", nil, @test_env
|
109
219
|
@test_store.mole_result[:user_id].should be_nil
|
110
|
-
@test_store.mole_result[:user_name].should == '
|
220
|
+
@test_store.mole_result[:user_name].should == 'fernand'
|
221
|
+
end
|
222
|
+
|
223
|
+
it "should extract a username correctly" do
|
224
|
+
@opts[:user_key] = { :session_key => :user_id, :extractor => lambda { |k| "Fernand #{k}" } }
|
225
|
+
app( @opts )
|
226
|
+
get "/", nil, @test_env
|
227
|
+
@test_store.mole_result[:user_id].should == 100
|
228
|
+
@test_store.mole_result[:user_name].should == 'Fernand 100'
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
describe "rails env" do
|
233
|
+
it "should find route info correctly" do
|
234
|
+
RAILS_ENV = true
|
235
|
+
ActionController::Routing::Routes.stub!( :recognize_path ).and_return( { :controller => 'fred', :action => 'blee' } )
|
236
|
+
rack = Rack::Mole.new( nil )
|
237
|
+
|
238
|
+
# routes.should_receive( 'recognize_path' ).with( 'fred', { :method => 'blee' } ).and_return( )
|
239
|
+
res = rack.send( :get_route, OpenStruct.new( :path => "/", :request_method => "GET") )
|
240
|
+
res.should_not be_nil
|
241
|
+
res[:controller].should == 'fred'
|
242
|
+
res[:action].should == 'blee'
|
111
243
|
end
|
112
244
|
end
|
113
245
|
|
246
|
+
# ---------------------------------------------------------------------------
|
247
|
+
describe 'sending alerts' do
|
248
|
+
it "should send out alerts on the first occurrance of a perf issue" do
|
249
|
+
Rackamole::Alert::Twitt.stub!( :deliver_alert )
|
250
|
+
Rackamole::Alert::Emole.stub!( :deliver_alert )
|
251
|
+
|
252
|
+
@opts[:twitter] = { :username => "fred", :password => "blee", :alert_on => [Rackamole.perf] }
|
253
|
+
@opts[:email] = { :from => "fred", :to => ["blee"], :alert_on => [Rackamole.perf] }
|
254
|
+
|
255
|
+
slow_app( @opts )
|
256
|
+
|
257
|
+
Rackamole::Alert::Emole.should_receive( :deliver_alert ).once
|
258
|
+
Rackamole::Alert::Twitt.should_receive( :deliver_alert ).once
|
259
|
+
|
260
|
+
get "/", nil, @test_env
|
261
|
+
end
|
262
|
+
|
263
|
+
it "should should not send several alerts on an occurance of the same issue" do
|
264
|
+
Rackamole::Alert::Twitt.stub!( :deliver_alert )
|
265
|
+
Rackamole::Alert::Emole.stub!( :deliver_alert )
|
266
|
+
|
267
|
+
@opts[:twitter] = { :username => "fred", :password => "blee", :alert_on => [Rackamole.perf] }
|
268
|
+
@opts[:email] = { :from => "fred", :to => ["blee"], :alert_on => [Rackamole.perf] }
|
269
|
+
|
270
|
+
slow_app( @opts )
|
271
|
+
|
272
|
+
env = @test_env
|
273
|
+
# First time ok
|
274
|
+
Rackamole::Alert::Emole.should_receive( :deliver_alert ).once
|
275
|
+
Rackamole::Alert::Twitt.should_receive( :deliver_alert ).once
|
276
|
+
get "/", nil, env
|
277
|
+
env = last_request.env
|
278
|
+
# Second time - no alerts
|
279
|
+
Rackamole::Alert::Emole.should_not_receive( :deliver_alert )
|
280
|
+
Rackamole::Alert::Twitt.should_not_receive( :deliver_alert )
|
281
|
+
get "/", nil, env
|
282
|
+
end
|
283
|
+
|
284
|
+
end
|
285
|
+
|
286
|
+
# ---------------------------------------------------------------------------
|
114
287
|
describe '#alertable?' do
|
115
288
|
before( :each ) do
|
116
289
|
@rack = Rack::Mole.new( nil,
|
@@ -121,7 +294,7 @@ describe Rack::Mole do
|
|
121
294
|
},
|
122
295
|
:email => {
|
123
296
|
:from => 'fred',
|
124
|
-
:to => 'blee',
|
297
|
+
:to => ['blee'],
|
125
298
|
:alert_on => [Rackamole.perf, Rackamole.fault, Rackamole.feature]
|
126
299
|
} )
|
127
300
|
end
|
@@ -143,6 +316,7 @@ describe Rack::Mole do
|
|
143
316
|
end
|
144
317
|
end
|
145
318
|
|
319
|
+
# ---------------------------------------------------------------------------
|
146
320
|
describe '#configured?' do
|
147
321
|
before( :each ) do
|
148
322
|
options = {
|
@@ -176,6 +350,7 @@ describe Rack::Mole do
|
|
176
350
|
end
|
177
351
|
end
|
178
352
|
|
353
|
+
# ---------------------------------------------------------------------------
|
179
354
|
describe '#id_browser' do
|
180
355
|
before :all do
|
181
356
|
@rack = Rack::Mole.new( nil )
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), %w[.. .. spec_helper])
|
2
|
+
require 'chronic'
|
3
|
+
|
4
|
+
describe Rackamole::Stash::Collector do
|
5
|
+
before( :each ) do
|
6
|
+
@now = Chronic.parse( "11/27/2009" )
|
7
|
+
@collector = Rackamole::Stash::Collector.new( "Fred", "test" )
|
8
|
+
end
|
9
|
+
|
10
|
+
describe "#stash" do
|
11
|
+
it "should record fault information correctly" do
|
12
|
+
begin
|
13
|
+
raise "Oh snap!"
|
14
|
+
rescue => boom
|
15
|
+
@collector.stash_fault( "/", boom.backtrace.first, @now )
|
16
|
+
@collector.send( :faults ).size.should == 1
|
17
|
+
|
18
|
+
fault = @collector.send( :find_fault, "/", boom.backtrace.first )
|
19
|
+
fault.should_not be_nil
|
20
|
+
fault.send( :path ).should == "/"
|
21
|
+
fault.send( :stack ).should == "./spec/rackamole/stash/collector_spec.rb:13"
|
22
|
+
fault.send( :timestamp ).should == @now
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should record perf information correctly" do
|
27
|
+
@collector.stash_perf( "/", 10.0, @now )
|
28
|
+
@collector.send( :perfs ).size.should == 1
|
29
|
+
|
30
|
+
perf = @collector.send( :find_perf, "/" )
|
31
|
+
perf.should_not be_nil
|
32
|
+
perf.send( :path ).should == "/"
|
33
|
+
perf.send( :elapsed ).should == 10.0
|
34
|
+
perf.send( :timestamp ).should == @now
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe "#expire" do
|
39
|
+
before( :all ) do
|
40
|
+
@now = Chronic.parse( "11/27/2009" )
|
41
|
+
@yesterday = Chronic.parse( "yesterday", :now => @now )
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should expire fault correctly" do
|
45
|
+
begin
|
46
|
+
raise "Oh snap!"
|
47
|
+
rescue => boom
|
48
|
+
@collector.stash_fault( "/", boom.backtrace.first, @yesterday )
|
49
|
+
@collector.send( :faults ).size.should == 1
|
50
|
+
@collector.expire_faults!
|
51
|
+
@collector.send( :faults ).size.should == 0
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should expire perf correctly" do
|
56
|
+
@collector.stash_perf( "/", 10, @yesterday )
|
57
|
+
@collector.send( :perfs ).size.should == 1
|
58
|
+
@collector.expire_perfs!
|
59
|
+
@collector.send( :perfs ).size.should == 0
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), %w[.. .. spec_helper])
|
2
|
+
require 'chronic'
|
3
|
+
|
4
|
+
describe Rackamole::Stash::Fault do
|
5
|
+
before( :all ) do
|
6
|
+
@now = Chronic.parse( "11/27/2009" )
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should record fault information correctly" do
|
10
|
+
begin
|
11
|
+
raise "Oh snap!"
|
12
|
+
rescue => boom
|
13
|
+
fault = Rackamole::Stash::Fault.new( "/", boom.backtrace.first, @now )
|
14
|
+
fault.send( :path ).should == "/"
|
15
|
+
fault.send( :stack ).should == "./spec/rackamole/stash/fault_spec.rb:11"
|
16
|
+
fault.send( :timestamp ).should == @now
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), %w[.. .. spec_helper])
|
2
|
+
require 'chronic'
|
3
|
+
|
4
|
+
describe Rackamole::Stash::Perf do
|
5
|
+
before( :all ) do
|
6
|
+
@now = Chronic.parse( "11/27/2009" )
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should record perf information correctly" do
|
10
|
+
perf = Rackamole::Stash::Perf.new( "/", 10.0, @now )
|
11
|
+
perf.send( :path ).should == "/"
|
12
|
+
perf.send( :elapsed ).should == 10.0
|
13
|
+
perf.send( :timestamp ).should == @now
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
File without changes
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rackamole
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Fernand Galiana
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-11-
|
12
|
+
date: 2009-11-28 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -42,6 +42,16 @@ dependencies:
|
|
42
42
|
- !ruby/object:Gem::Version
|
43
43
|
version: 0.17.1
|
44
44
|
version:
|
45
|
+
- !ruby/object:Gem::Dependency
|
46
|
+
name: chronic
|
47
|
+
type: :runtime
|
48
|
+
version_requirement:
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 0.2.3
|
54
|
+
version:
|
45
55
|
- !ruby/object:Gem::Dependency
|
46
56
|
name: twitter4r
|
47
57
|
type: :runtime
|
@@ -106,10 +116,13 @@ files:
|
|
106
116
|
- lib/rackamole/alert/emole.rb
|
107
117
|
- lib/rackamole/alert/templates/rackamole/alert/emole/alert.erb
|
108
118
|
- lib/rackamole/alert/twitt.rb
|
109
|
-
- lib/rackamole/config.rb
|
110
119
|
- lib/rackamole/interceptor.rb
|
111
120
|
- lib/rackamole/logger.rb
|
112
121
|
- lib/rackamole/mole.rb
|
122
|
+
- lib/rackamole/stash/base.rb
|
123
|
+
- lib/rackamole/stash/collector.rb
|
124
|
+
- lib/rackamole/stash/fault.rb
|
125
|
+
- lib/rackamole/stash/perf.rb
|
113
126
|
- lib/rackamole/store.rb
|
114
127
|
- lib/rackamole/store/log.rb
|
115
128
|
- lib/rackamole/store/mongo_db.rb
|
@@ -164,10 +177,16 @@ files:
|
|
164
177
|
- samples/rails/moled/script/server
|
165
178
|
- samples/rails/moled/test/performance/browsing_test.rb
|
166
179
|
- samples/rails/moled/test/test_helper.rb
|
167
|
-
- samples/sinatra/blee.rb
|
168
180
|
- samples/sinatra/moled.rb
|
181
|
+
- samples/sinatra/public/.DS_Store
|
182
|
+
- samples/sinatra/public/images/mole_logo.png
|
183
|
+
- samples/sinatra/public/stylesheets/styles.css
|
184
|
+
- samples/sinatra/views/index.erb
|
185
|
+
- samples/sinatra/views/layout.erb
|
169
186
|
- samples/sinatra/views/normal.erb
|
170
187
|
- samples/sinatra/views/params.erb
|
188
|
+
- samples/sinatra/views/post.erb
|
189
|
+
- samples/sinatra/views/slow.erb
|
171
190
|
- spec/expected_results/mole_exception.log
|
172
191
|
- spec/expected_results/mole_feature.log
|
173
192
|
- spec/expected_results/mole_perf.log
|
@@ -176,6 +195,9 @@ files:
|
|
176
195
|
- spec/rackamole/interceptor_spec.rb
|
177
196
|
- spec/rackamole/logger_spec.rb
|
178
197
|
- spec/rackamole/mole_spec.rb
|
198
|
+
- spec/rackamole/stash/collector_spec.rb
|
199
|
+
- spec/rackamole/stash/fault_spec.rb
|
200
|
+
- spec/rackamole/stash/perf_spec.rb
|
179
201
|
- spec/rackamole/store/log_spec.rb
|
180
202
|
- spec/rackamole/store/mongo_db_spec.rb
|
181
203
|
- spec/rackamole_spec.rb
|
@@ -194,6 +216,7 @@ files:
|
|
194
216
|
- tasks/zentest.rake
|
195
217
|
- z_experiments/config.rb
|
196
218
|
- z_experiments/config_sample.rb
|
219
|
+
- z_experiments/configuration.rb
|
197
220
|
has_rdoc: true
|
198
221
|
homepage: http://www.rackamole.com
|
199
222
|
licenses: []
|