rackamole 0.0.6 → 0.0.7
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +1 -1
- data/Rakefile +3 -1
- data/aa.txt +10 -0
- data/lib/rackamole.rb +6 -5
- data/lib/rackamole/alert/emole.rb +67 -0
- data/lib/rackamole/alert/templates/rackamole/alert/emole/alert.erb +9 -0
- data/lib/rackamole/alert/twitt.rb +73 -0
- data/lib/rackamole/interceptor.rb +7 -1
- data/lib/rackamole/logger.rb +5 -2
- data/lib/rackamole/mole.rb +116 -35
- data/lib/rackamole/store.rb +4 -0
- data/lib/rackamole/store/log.rb +55 -50
- data/lib/rackamole/store/mongo_db.rb +46 -26
- data/spec/expected_results/mole_exception.log +3 -2
- data/spec/expected_results/mole_feature.log +2 -2
- data/spec/expected_results/mole_perf.log +2 -3
- data/spec/rackamole/alert/emole_spec.rb +109 -0
- data/spec/rackamole/alert/twitt_spec.rb +68 -0
- data/spec/rackamole/mole_spec.rb +61 -9
- data/spec/rackamole/store/log_spec.rb +5 -3
- data/spec/rackamole/store/mongo_db_spec.rb +12 -8
- data/z_experiments/config.rb +38 -0
- metadata +31 -2
data/lib/rackamole/store.rb
CHANGED
data/lib/rackamole/store/log.rb
CHANGED
@@ -1,57 +1,62 @@
|
|
1
|
-
module Rackamole
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
1
|
+
module Rackamole::Store
|
2
|
+
class Log
|
3
|
+
|
4
|
+
# Stores mole information to a log file or dump it to the console. All
|
5
|
+
# available mole info will dumped to the logger.
|
6
|
+
#
|
7
|
+
# === Params:
|
8
|
+
# file_name :: Specifies a file to send the logs to. By default mole info
|
9
|
+
# will be sent out to stdout.
|
10
|
+
def initialize( file_name=$stdout )
|
11
|
+
@logger = Rackamole::Logger.new( :log_file => file_name )
|
12
|
+
end
|
13
|
+
|
14
|
+
# Dump mole info to logger
|
15
|
+
#
|
16
|
+
# === Params:
|
17
|
+
# attrs :: The available moled information for a given feature
|
18
|
+
def mole( attrs )
|
19
|
+
return if attrs.empty?
|
20
|
+
|
21
|
+
display_head( attrs )
|
22
|
+
display_commons( attrs )
|
23
|
+
rescue => mole_boom
|
24
|
+
log.error "MOLE STORE CRAPPED OUT -- #{mole_boom}"
|
25
|
+
log.error mole_boom.backtrace.join( "\n " )
|
26
|
+
end
|
27
|
+
|
28
|
+
# =======================================================================
|
29
|
+
private
|
30
|
+
|
31
|
+
# dump moled info to log
|
32
|
+
def display_commons( args )
|
33
|
+
args.each do |k,v|
|
34
|
+
display_info( k.to_s.capitalize, v.inspect )
|
35
|
+
end
|
8
36
|
end
|
9
37
|
|
10
|
-
#
|
11
|
-
def
|
12
|
-
|
13
|
-
|
14
|
-
if args[:stack]
|
15
|
-
display_head "MOLED EXCEPTION"
|
16
|
-
elsif args[:performance]
|
17
|
-
display_head "MOLED PERFORMANCE"
|
18
|
-
else
|
19
|
-
display_head "MOLED FEATURE"
|
20
|
-
end
|
21
|
-
display_commons( args )
|
22
|
-
rescue => mole_boom
|
23
|
-
log.error "MOLE STORE CRAPPED OUT -- #{mole_boom}"
|
24
|
-
log.error mole_boom.backtrace.join( "\n " )
|
38
|
+
# retrieves logger instance
|
39
|
+
def log
|
40
|
+
@logger
|
25
41
|
end
|
26
|
-
|
27
|
-
#
|
28
|
-
|
29
|
-
|
30
|
-
# dump moled info to log
|
31
|
-
def display_commons( args )
|
32
|
-
args.each do |k,v|
|
33
|
-
display_info( k.to_s.capitalize, v.inspect )
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
# retrieves logger instance
|
38
|
-
def log
|
39
|
-
@logger
|
40
|
-
end
|
41
|
-
|
42
|
-
# Console layout spacer
|
43
|
-
def spacer() 20; end
|
42
|
+
|
43
|
+
# Console layout spacer
|
44
|
+
def spacer() 20; end
|
44
45
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
46
|
+
# Display mole type
|
47
|
+
def display_head( args )
|
48
|
+
log.info "-"*100
|
49
|
+
log.info case args[:type]
|
50
|
+
when Rackamole.feature : "FEATURE m()le"
|
51
|
+
when Rackamole.fault : "FAULT m()le"
|
52
|
+
when Rackamole.perf : "PERFORMANCE m()le"
|
53
|
+
else "Unknown type #{args[:type].inspect}"
|
49
54
|
end
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
55
|
+
end
|
56
|
+
|
57
|
+
# Output formating...
|
58
|
+
def display_info( key, value )
|
59
|
+
log.info "%-#{spacer}s : %s" % [key, value]
|
60
|
+
end
|
56
61
|
end
|
57
62
|
end
|
@@ -4,28 +4,36 @@ require 'mongo'
|
|
4
4
|
# BOZO !! Deal with indexes here ?
|
5
5
|
module Rackamole
|
6
6
|
module Store
|
7
|
-
# Mongo adapter. Stores mole info
|
7
|
+
# Mongo adapter. Stores mole info to a mongo database.
|
8
8
|
class MongoDb
|
9
9
|
|
10
|
-
attr_reader :database, :logs, :features
|
11
|
-
|
12
|
-
#
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
10
|
+
attr_reader :database, :logs, :features #:nodoc:
|
11
|
+
|
12
|
+
# Initializes the mongo db store. MongoDb can be used as a persitent store for
|
13
|
+
# mole information. This is a preferred store for the mole as it will allow you
|
14
|
+
# to gather up reports based on application usage, perf or faults...
|
15
|
+
#
|
16
|
+
# Setup rackamole to use the mongodb store as follows:
|
17
|
+
#
|
18
|
+
# config.middleware.use Rack::Mole, { :store => Rackamole::Store::MongoDb.new }
|
19
|
+
#
|
20
|
+
# === Options
|
21
|
+
#
|
22
|
+
# :host :: The name of the host running the mongo server. Default: localhost
|
23
|
+
# :port :: The port for the mongo server instance. Default: 27017
|
24
|
+
# :database :: The name of the mole databaase. Default: mole_mdb
|
25
|
+
#
|
17
26
|
def initialize( options={} )
|
18
27
|
opts = default_options.merge( options )
|
28
|
+
validate_options( opts )
|
19
29
|
init_mongo( opts )
|
20
30
|
end
|
21
|
-
|
22
|
-
#
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
# Dump mole info to logger
|
31
|
+
|
32
|
+
# Dump mole info to a mongo database. There are actually 2 collections
|
33
|
+
# for mole information. Namely features and logs. The features collection hold
|
34
|
+
# application and feature information and is referenced in the mole log. The logs
|
35
|
+
# collections holds all information that was gathered during the request
|
36
|
+
# such as user, params, session, request time, etc...
|
29
37
|
def mole( arguments )
|
30
38
|
return if arguments.empty?
|
31
39
|
|
@@ -41,6 +49,12 @@ module Rackamole
|
|
41
49
|
|
42
50
|
# =======================================================================
|
43
51
|
private
|
52
|
+
|
53
|
+
# Clear out mole database content ( Careful there - testing only! )
|
54
|
+
def reset!
|
55
|
+
logs.remove
|
56
|
+
features.remove
|
57
|
+
end
|
44
58
|
|
45
59
|
def init_mongo( opts )
|
46
60
|
@connection = Mongo::Connection.new( opts[:host], opts[:port], :logger => opts[:logger] )
|
@@ -48,6 +62,15 @@ module Rackamole
|
|
48
62
|
@features = database.collection( 'features' )
|
49
63
|
@logs = database.collection( 'logs' )
|
50
64
|
end
|
65
|
+
|
66
|
+
# Validates option hash.
|
67
|
+
def validate_options( opts )
|
68
|
+
%w[host port database].each do |option|
|
69
|
+
unless opts[option.to_sym]
|
70
|
+
raise "[MOle] Mongo store configuration error -- You must specify a value for option `:#{option}"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
51
74
|
|
52
75
|
# Set up mongo default options ie localhost host, default mongo port and
|
53
76
|
# the database being mole_mdb
|
@@ -65,7 +88,7 @@ module Rackamole
|
|
65
88
|
route_info = args.delete( :route_info )
|
66
89
|
environment = args.delete( :environment )
|
67
90
|
|
68
|
-
row = { min_field(:app_name) => app_name, min_field(:env) => environment }
|
91
|
+
row = { min_field(:app_name) => app_name, min_field(:env) => environment.to_s }
|
69
92
|
if route_info
|
70
93
|
row[min_field(:controller)] = route_info[:controller]
|
71
94
|
row[min_field(:action)] = route_info[:action]
|
@@ -81,17 +104,12 @@ module Rackamole
|
|
81
104
|
end
|
82
105
|
|
83
106
|
# Insert a new feature in the db
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
type = PERFORMANCE if args.delete(:performance)
|
88
|
-
|
89
|
-
# BOZO !! to reduce collection space...
|
90
|
-
# Using cryptic key to reduce storage needs.
|
91
|
-
# Also narrowing date/time to ints
|
107
|
+
# NOTE : Using min key to reduce storage needs. I know not that great for higher level api's :-(
|
108
|
+
# also saving date and time as ints. same deal...
|
109
|
+
def save_log( feature, args )
|
92
110
|
now = Time.now
|
93
111
|
row = {
|
94
|
-
min_field( :type ) => type,
|
112
|
+
min_field( :type ) => args[:type],
|
95
113
|
min_field( :feature_id ) => feature['_id'].to_s,
|
96
114
|
min_field( :date_id ) => ("%4d%02d%02d" %[now.year, now.month, now.day]).to_i,
|
97
115
|
min_field( :time_id ) => ("%02d%02d%02d" %[now.hour, now.min, now.sec] ).to_i
|
@@ -111,6 +129,7 @@ module Rackamole
|
|
111
129
|
# Normalize all accessors to 3 chars.
|
112
130
|
def field_map
|
113
131
|
@field_map ||= {
|
132
|
+
:env => :env,
|
114
133
|
:app_name => :app,
|
115
134
|
:context => :ctx,
|
116
135
|
:controller => :ctl,
|
@@ -131,6 +150,7 @@ module Rackamole
|
|
131
150
|
:session => :ses,
|
132
151
|
:params => :par,
|
133
152
|
:ruby_version => :ver,
|
153
|
+
:fault => :msg,
|
134
154
|
:stack => :sta
|
135
155
|
}
|
136
156
|
end
|
@@ -1,8 +1,8 @@
|
|
1
1
|
----------------------------------------------------------------------------------------------------
|
2
|
-
|
2
|
+
FAULT m()le
|
3
|
+
Type : 2
|
3
4
|
App_name : "Test app"
|
4
5
|
Environment : :test
|
5
|
-
Perf_issue : false
|
6
6
|
Ip : "1.1.1.1"
|
7
7
|
Browser : "Ibrowse"
|
8
8
|
User_id : 100
|
@@ -13,5 +13,6 @@ Path : "/fred"
|
|
13
13
|
Method : "GET"
|
14
14
|
Params : {:blee=>"\"duh\""}
|
15
15
|
Session : {:fred=>"10"}
|
16
|
+
Fault : "Shiet"
|
16
17
|
Stack : ["Oh snap!"]
|
17
18
|
Ruby_version : "ruby 1.8.6 (2007-03-13 patchlevel 0) [i686-darwin8.10.1]"
|
@@ -1,8 +1,8 @@
|
|
1
1
|
----------------------------------------------------------------------------------------------------
|
2
|
-
|
2
|
+
FEATURE m()le
|
3
|
+
Type : 0
|
3
4
|
App_name : "Test app"
|
4
5
|
Environment : :test
|
5
|
-
Perf_issue : false
|
6
6
|
Ip : "1.1.1.1"
|
7
7
|
Browser : "Ibrowse"
|
8
8
|
User_id : 100
|
@@ -1,8 +1,8 @@
|
|
1
1
|
----------------------------------------------------------------------------------------------------
|
2
|
-
|
2
|
+
PERFORMANCE m()le
|
3
|
+
Type : 1
|
3
4
|
App_name : "Test app"
|
4
5
|
Environment : :test
|
5
|
-
Perf_issue : false
|
6
6
|
Ip : "1.1.1.1"
|
7
7
|
Browser : "Ibrowse"
|
8
8
|
User_id : 100
|
@@ -13,4 +13,3 @@ Path : "/fred"
|
|
13
13
|
Method : "GET"
|
14
14
|
Params : {:blee=>"\"duh\""}
|
15
15
|
Session : {:fred=>"10"}
|
16
|
-
Performance : true
|
@@ -0,0 +1,109 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), %w[.. .. spec_helper])
|
2
|
+
|
3
|
+
describe Rackamole::Alert::Emole do
|
4
|
+
|
5
|
+
before( :each ) do
|
6
|
+
@from = "fernand"
|
7
|
+
@to = %w[fernand bobo blee]
|
8
|
+
|
9
|
+
@expected = TMail::Mail.new
|
10
|
+
@expected.set_content_type 'text', 'plain', { 'charset' => 'utf-8' }
|
11
|
+
@expected.mime_version = '1.0'
|
12
|
+
@expected.from = @from
|
13
|
+
@expected.to = @to
|
14
|
+
|
15
|
+
@args = OrderedHash.new
|
16
|
+
@args[:type] = Rackamole.feature
|
17
|
+
@args[:app_name] = 'Test'
|
18
|
+
@args[:host] = 'Fred'
|
19
|
+
@args[:user_name] = 'Fernand'
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "#alert" do
|
23
|
+
|
24
|
+
it "should send a feature email correctly" do
|
25
|
+
@expected.subject = "[M()le] (Feature) -Test@Fred- for user Fernand"
|
26
|
+
@expected.body = feature_body
|
27
|
+
Rackamole::Alert::Emole.create_alert( @from, @to, @args ).encoded.should == @expected.encoded
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should send a perf email correctly" do
|
31
|
+
@args[:type] = Rackamole.perf
|
32
|
+
@args[:request_time] = 10.0
|
33
|
+
@expected.subject = "[M()le] (Performance:10.0) -Test@Fred- for user Fernand"
|
34
|
+
@expected.body = perf_body
|
35
|
+
Rackamole::Alert::Emole.create_alert( @from, @to, @args ).encoded.should == @expected.encoded
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should send a fault email correctly" do
|
39
|
+
@args[:type] = Rackamole.fault
|
40
|
+
@args[:fault] = 'Oh Snap!'
|
41
|
+
@args[:stack] = ['fred', 'blee']
|
42
|
+
@args[:params] = { :id => 10 }
|
43
|
+
@expected.subject = "[M()le] (Fault) -Test@Fred- for user Fernand"
|
44
|
+
@expected.body = fault_body
|
45
|
+
Rackamole::Alert::Emole.create_alert( @from, @to, @args ).encoded.should == @expected.encoded
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
def feature_body
|
52
|
+
msg=<<MSG
|
53
|
+
A watched feature was triggered in application `Test on host `Fred
|
54
|
+
|
55
|
+
Details...
|
56
|
+
|
57
|
+
type 0
|
58
|
+
app_name \"Test\"
|
59
|
+
host \"Fred\"
|
60
|
+
user_name \"Fernand\"
|
61
|
+
|
62
|
+
- Your Rackamole
|
63
|
+
|
64
|
+
This message was generated automatically. Please do not respond directly.
|
65
|
+
MSG
|
66
|
+
end
|
67
|
+
|
68
|
+
def perf_body
|
69
|
+
msg=<<MSG
|
70
|
+
A watched feature was triggered in application `Test on host `Fred
|
71
|
+
|
72
|
+
Details...
|
73
|
+
|
74
|
+
type 1
|
75
|
+
app_name \"Test\"
|
76
|
+
host \"Fred\"
|
77
|
+
user_name \"Fernand\"
|
78
|
+
request_time 10.0
|
79
|
+
|
80
|
+
- Your Rackamole
|
81
|
+
|
82
|
+
This message was generated automatically. Please do not respond directly.
|
83
|
+
MSG
|
84
|
+
end
|
85
|
+
|
86
|
+
def fault_body
|
87
|
+
msg=<<MSG
|
88
|
+
A watched feature was triggered in application `Test on host `Fred
|
89
|
+
|
90
|
+
Details...
|
91
|
+
|
92
|
+
type 2
|
93
|
+
app_name \"Test\"
|
94
|
+
host \"Fred\"
|
95
|
+
user_name \"Fernand\"
|
96
|
+
fault \"Oh Snap!\"
|
97
|
+
stack
|
98
|
+
fred
|
99
|
+
blee
|
100
|
+
params
|
101
|
+
id 10
|
102
|
+
|
103
|
+
- Your Rackamole
|
104
|
+
|
105
|
+
This message was generated automatically. Please do not respond directly.
|
106
|
+
MSG
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), %w[.. .. spec_helper])
|
2
|
+
|
3
|
+
describe Rackamole::Alert::Twitt do
|
4
|
+
before( :each ) do
|
5
|
+
@alert = Rackamole::Alert::Twitt.new( 'fernand', "blee" )
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should truncate a message correctly" do
|
9
|
+
@alert.send( :truncate, "a"*141 ).should == "a"*137 + '...'
|
10
|
+
end
|
11
|
+
|
12
|
+
describe '#display_feature' do
|
13
|
+
it "should display a rails feature correctly" do
|
14
|
+
@alert.send( :display_feature, :route_info => { :controller => 'fred', :action => 'blee'} ).should == "fred#blee"
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should display a path feature for other rack framword" do
|
18
|
+
@alert.send( :display_feature, :path => '/fred/blee' ).should == "/fred/blee"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe '#send_alert' do
|
23
|
+
before( :each ) do
|
24
|
+
@args = OrderedHash.new
|
25
|
+
@args[:type] = Rackamole.feature
|
26
|
+
@args[:app_name] = 'Test'
|
27
|
+
@args[:host] = 'Fred'
|
28
|
+
@args[:user_name] = 'Fernand'
|
29
|
+
@args[:path] = '/blee/fred'
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should twitt a feature alert correctly" do
|
33
|
+
client = stub( Twitter::Client )
|
34
|
+
|
35
|
+
@alert.should_receive( :twitt ).once.and_return( client )
|
36
|
+
# client.should_receive( :new ).exactly(1).with( 'fernand', 'blee' )
|
37
|
+
client.should_receive( :status ).once
|
38
|
+
|
39
|
+
@alert.send_alert( @args ).should == "[Feature] -- Test:Fred\nFernand : /blee/fred"
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should twitt a perf alert correctly" do
|
43
|
+
@args[:type] = Rackamole.perf
|
44
|
+
@args[:request_time] = 10.0
|
45
|
+
|
46
|
+
client = stub( Twitter::Client )
|
47
|
+
|
48
|
+
@alert.should_receive( :twitt ).once.and_return( client )
|
49
|
+
client.should_receive( :status ).once
|
50
|
+
|
51
|
+
@alert.send_alert( @args ).should == "[Perf] -- Test:Fred\nFernand : /blee/fred : 10.0 secs"
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should twitt a perf alert correctly" do
|
55
|
+
@args[:type] = Rackamole.fault
|
56
|
+
@args[:fault] = 'Oh snap!'
|
57
|
+
|
58
|
+
client = stub( Twitter::Client )
|
59
|
+
|
60
|
+
@alert.should_receive( :twitt ).once.and_return( client )
|
61
|
+
client.should_receive( :status ).once
|
62
|
+
|
63
|
+
@alert.send_alert( @args ).should == "[Fault] -- Test:Fred\nFernand : /blee/fred : Oh snap!"
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|