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