rackamole 0.0.6 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -1 +1,5 @@
1
+ # Provides various transient/persistent storage for the mole.
2
+ # Currently, you can send moled info to the console, a file, or a mongo database.
3
+ # Please see the various stores for more info...
4
+ #
1
5
  Rackamole.require_all_libs_relative_to(__FILE__)
@@ -1,57 +1,62 @@
1
- module Rackamole
2
- module Store
3
- # Logger adapter. Stores mole information to a log file or dump it to stdout
4
- class Log
5
-
6
- def initialize( file_name=$stdout )
7
- @logger = Rackamole::Logger.new( :log_file => file_name )
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
- # Dump mole info to logger
11
- def mole( args )
12
- return if args.empty?
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
- private
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
- # Display mole type
46
- def display_head( msg )
47
- log.info "-"*100
48
- log.info msg
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
- # Output formating...
52
- def display_info( key, value )
53
- log.info "%-#{spacer}s : %s" % [key, value]
54
- end
55
- end
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 in a mongo database.
7
+ # Mongo adapter. Stores mole info to a mongo database.
8
8
  class MongoDb
9
9
 
10
- attr_reader :database, :logs, :features
11
-
12
- # Defines the various feature types
13
- FEATURE = 0
14
- PERFORMANCE = 1
15
- EXCEPTION = 2
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
- # clear out db content ( used in testing... )
23
- def reset!
24
- logs.remove
25
- features.remove
26
- end
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
- def save_log( feature, args )
85
- type = FEATURE
86
- type = EXCEPTION if args[:stack]
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
- MOLED EXCEPTION
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
- MOLED FEATURE
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
- MOLED PERFORMANCE
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