rackamole 0.0.6 → 0.0.7
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/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
|