mole 0.0.1
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/History.txt +4 -0
- data/Manifest.txt +45 -0
- data/README.txt +140 -0
- data/Rakefile +47 -0
- data/bin/mole +8 -0
- data/bin/molify +64 -0
- data/config/database.yml +21 -0
- data/config/test_database.yml +69 -0
- data/lib/mole/db/migrate.rb +90 -0
- data/lib/mole/e_mole.rb +74 -0
- data/lib/mole/logger.rb +131 -0
- data/lib/mole/models/mole_feature.rb +45 -0
- data/lib/mole/models/mole_log.rb +56 -0
- data/lib/mole/module.rb +274 -0
- data/lib/mole/moler.rb +51 -0
- data/lib/mole/utils/frameworks.rb +22 -0
- data/lib/mole/version.rb +15 -0
- data/lib/mole.rb +175 -0
- data/spec/data/blee.rb +53 -0
- data/spec/db/migrate_spec.rb +19 -0
- data/spec/emole_spec.rb +43 -0
- data/spec/logger_spec.rb +56 -0
- data/spec/models/mole_feature_spec.rb +37 -0
- data/spec/models/mole_log_spec.rb +73 -0
- data/spec/module_spec.rb +171 -0
- data/spec/mole_spec.rb +38 -0
- data/spec/moler_spec.rb +65 -0
- data/spec/spec_helper.rb +59 -0
- data/spec/utils/framework_spec.rb +46 -0
- data/tasks/ann.rake +76 -0
- data/tasks/annotations.rake +22 -0
- data/tasks/doc.rake +48 -0
- data/tasks/gem.rake +110 -0
- data/tasks/manifest.rake +49 -0
- data/tasks/mole.rake +115 -0
- data/tasks/post_load.rake +26 -0
- data/tasks/rubyforge.rake +57 -0
- data/tasks/setup.rb +227 -0
- data/tasks/spec.rake +54 -0
- data/tasks/svn.rake +44 -0
- data/tasks/test.rake +38 -0
- data/templates/mole/e_mole/exception_alerts.rhtml +14 -0
- data/templates/mole/e_mole/feature_alerts.rhtml +11 -0
- data/templates/mole/e_mole/perf_alerts.rhtml +12 -0
- data/test/test_mole.rb +0 -0
- metadata +120 -0
data/lib/mole/logger.rb
ADDED
@@ -0,0 +1,131 @@
|
|
1
|
+
require 'logging'
|
2
|
+
require 'forwardable'
|
3
|
+
|
4
|
+
module Mole
|
5
|
+
class Logger
|
6
|
+
class ConfigurationError < StandardError ; end #:nodoc:
|
7
|
+
|
8
|
+
attr_reader :log # here for testing, don't really use it.
|
9
|
+
|
10
|
+
extend Forwardable
|
11
|
+
def_delegators :@log, :debug, :warn, :info, :error, :fatal
|
12
|
+
def_delegators :@log, :level=, :level
|
13
|
+
def_delegators :@log, :debug?, :warn?, :info?, :error?, :fatal?
|
14
|
+
def_delegators :@log, :add, :clear_appenders
|
15
|
+
|
16
|
+
# there are more options here than are typically utilized for the
|
17
|
+
# logger, they are made available if someone wants to utilize
|
18
|
+
# them directly.
|
19
|
+
#
|
20
|
+
def self.default_options #:nodoc:
|
21
|
+
@default_options ||= {
|
22
|
+
|
23
|
+
# log event layout pattern
|
24
|
+
# YYYY-MM-DDTHH:MM:SS 12345 LEVEL LoggerName : The Log message
|
25
|
+
#
|
26
|
+
:layout_pattern => '%d %5p %5l %c : %m\n'.freeze ,
|
27
|
+
:logger_name => Mole ,
|
28
|
+
:additive => true ,
|
29
|
+
|
30
|
+
# log file configuration options
|
31
|
+
# age -> how often to rotate the logs if a file is used
|
32
|
+
# keep_count -> how many of the log files to keep
|
33
|
+
:log_level => :info ,
|
34
|
+
:log_file => $stdout ,
|
35
|
+
:log_file_age => 'daily'.freeze ,
|
36
|
+
:log_file_keep_count => 7 ,
|
37
|
+
|
38
|
+
# email logging options
|
39
|
+
# buffsize -> number of log events used as a threshold to send an
|
40
|
+
# email. If that is not reached before the program
|
41
|
+
# exists then the at_exit handler for logging will flush
|
42
|
+
# the log to smtp.
|
43
|
+
:email_alerts_to => nil ,
|
44
|
+
:email_alert_level => :error ,
|
45
|
+
:email_alert_server => "mail.collectiveintellect.com".freeze ,
|
46
|
+
:email_alert_buffsize => 200 ,
|
47
|
+
}
|
48
|
+
end
|
49
|
+
|
50
|
+
# create a new logger
|
51
|
+
#
|
52
|
+
def initialize( opts = {} )
|
53
|
+
@options = ::Mole::Logger.default_options.merge(opts)
|
54
|
+
@log = ::Logging::Logger[@options[:logger_name]]
|
55
|
+
@layout = ::Logging::Layouts::Pattern.new( { :pattern => @options[:layout_pattern] } )
|
56
|
+
|
57
|
+
# add appenders explicitly, since this logger may already be defined and
|
58
|
+
# already have loggers
|
59
|
+
@appenders = []
|
60
|
+
@appenders << log_file_appender if @options[:log_file]
|
61
|
+
@appenders << email_appender if @options[:email_alerts_to]
|
62
|
+
|
63
|
+
@log.appenders = @appenders
|
64
|
+
@log.level = @options[:log_level]
|
65
|
+
@log.additive = @options[:additive]
|
66
|
+
end
|
67
|
+
|
68
|
+
# File appender, this is either an IO appender or a RollingFile appender
|
69
|
+
# depending on if an IO object or a String is passed in.
|
70
|
+
#
|
71
|
+
# a configuration error is raised in any other circumstance
|
72
|
+
def log_file_appender #:nodoc:
|
73
|
+
appender_name = "#{@log.name}-log_file_appender"
|
74
|
+
if String === @options[:log_file] then
|
75
|
+
::Logging::Appenders::RollingFile.new( appender_name,
|
76
|
+
{ :layout => @layout,
|
77
|
+
:filename => @options[:log_file],
|
78
|
+
:age => @options[:log_file_age],
|
79
|
+
:keep => @options[:log_file_keep_count],
|
80
|
+
:safe => true # make sure log files are rolled using lockfile
|
81
|
+
})
|
82
|
+
elsif @options[:log_file].respond_to?(:print) then
|
83
|
+
::Logging::Appenders::IO.new( appender_name, @options[:log_file], :layout => @layout )
|
84
|
+
else
|
85
|
+
raise ConfigurationError, "Invalid :log_file option [#{@options[:log_file].inspect}]"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# an email appender that uses :email_alerts_to option to send emails to.
|
90
|
+
# :email_alerts_to can either be a singe email address as a string or an
|
91
|
+
# Array of email addresses. Any other option for :email_alerts_to is
|
92
|
+
# invalid and raises an error.
|
93
|
+
#
|
94
|
+
def email_appender #:nodoc:
|
95
|
+
email_alerts_to = [ @options[:email_alerts_to] ].flatten.reject { |x| x == nil }
|
96
|
+
raise ConfigurationError, "Invalid :email_alerts_to option [#{@options[:email_alerts_to].inspect}]" unless email_alerts_to.size > 0
|
97
|
+
::Logging::Appenders::Email.new("#{@log.name}-email_appender",
|
98
|
+
{
|
99
|
+
:level => @options[:email_alert_level],
|
100
|
+
:layout => @layout,
|
101
|
+
:from => "#{@log.name}",
|
102
|
+
:to => "#{email_alerts_to.join(', ')}",
|
103
|
+
:subject => "Logging Alert from #{@log.name} on #{ENV['HOSTNAME']}",
|
104
|
+
:server => @options[:email_alert_server],
|
105
|
+
:buffsize => @options[:email_alert_buffsize], # lines
|
106
|
+
})
|
107
|
+
end
|
108
|
+
|
109
|
+
# create a new logger thi logger has no options when it is created although
|
110
|
+
# more can be added with the logger instance that is returned. The
|
111
|
+
# appenders of the current instance of Logger will be set on the new
|
112
|
+
# logger and the options of the current logger will be applied
|
113
|
+
def for( arg ) #:nodoc:
|
114
|
+
new_logger = ::Logging::Logger[arg]
|
115
|
+
new_logger.level = @options[:level]
|
116
|
+
new_logger.additive = @options[:additive]
|
117
|
+
new_logger.appenders = @appenders
|
118
|
+
return new_logger
|
119
|
+
end
|
120
|
+
|
121
|
+
# mole the bastard - create an entry into MOle log file
|
122
|
+
def log_it( context, feature, user_id, args )
|
123
|
+
args ||= "no args"
|
124
|
+
user_id ||= "N/A"
|
125
|
+
ip_addr, browser_type = MoleLog.log_details( context )
|
126
|
+
info = []
|
127
|
+
args.keys.sort { |a,b| a.to_s <=> b.to_s }.each { |k| info << "#{k}=>#{args[k]}" }
|
128
|
+
self.info( "[#{ip_addr}/#{browser_type}]-- #{context}(#{feature}) --- #{user_id} -> #{info.join( ', ' ) }" )
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# Feature model - Tracks the various application features in the db.
|
2
|
+
class MoleFeature < ActiveRecord::Base
|
3
|
+
has_many :mole_logs
|
4
|
+
|
5
|
+
class << self
|
6
|
+
# famous constants...
|
7
|
+
def all() "ALL" ; end
|
8
|
+
def exception() "Exception" ; end
|
9
|
+
def performance() "Performance"; end
|
10
|
+
|
11
|
+
# find performance feature
|
12
|
+
def find_performance_feature( app_name )
|
13
|
+
find_feature( performance, app_name )
|
14
|
+
end
|
15
|
+
|
16
|
+
# find exception feature
|
17
|
+
def find_exception_feature( app_name )
|
18
|
+
find_feature( exception, app_name )
|
19
|
+
end
|
20
|
+
|
21
|
+
def find_all_feature( app_name )
|
22
|
+
find_feature( all, app_name )
|
23
|
+
end
|
24
|
+
|
25
|
+
# Finds all the features available for a given application
|
26
|
+
def find_features( app_name )
|
27
|
+
# Creates the all feature if necessary
|
28
|
+
find_all_feature( app_name )
|
29
|
+
MoleFeature.find( :all,
|
30
|
+
:conditions => ["app_name = ?", app_name],
|
31
|
+
:select => "id, name, context",
|
32
|
+
:order => "name asc" )
|
33
|
+
end
|
34
|
+
|
35
|
+
# locates an existing feature or create a new one
|
36
|
+
def find_feature( name, app_name, ctx_name=nil )
|
37
|
+
if name.nil? or name.empty?
|
38
|
+
::Mole.logger.error( "--- MOLE ERROR - Invalid feature. Empty or nil" )
|
39
|
+
return nil
|
40
|
+
end
|
41
|
+
find_by_name_and_context_and_app_name( name, ctx_name, app_name ) ||
|
42
|
+
create(:name => name,:context => ctx_name, :app_name => app_name )
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# Log Model - Tracks and record user interactions with various features
|
2
|
+
# This will make it easy to write an app on top on the MOle to track a
|
3
|
+
# particular application utilization.
|
4
|
+
class MoleLog < ActiveRecord::Base
|
5
|
+
belongs_to :mole_feature
|
6
|
+
|
7
|
+
class << self
|
8
|
+
# logs unchecked exception
|
9
|
+
def check_it( context, user_id, args )
|
10
|
+
if args[:boom]
|
11
|
+
args[:trace] = dump_stack( args[:boom] )
|
12
|
+
args[:boom] = args[:boom].to_s
|
13
|
+
end
|
14
|
+
log_it( context, MoleFeature::find_exception_feature( ::Mole.application ), user_id, args ) if ::Mole::moleable?
|
15
|
+
end
|
16
|
+
|
17
|
+
# logs perf occurence
|
18
|
+
def perf_it( context, user_id, args )
|
19
|
+
log_it( context, MoleFeature::find_performance_feature( ::Mole.application ), user_id, args ) if ::Mole::moleable?
|
20
|
+
end
|
21
|
+
|
22
|
+
# persists mole information to the database.
|
23
|
+
# This call will store information on the mole dedicated tables namely
|
24
|
+
# mole_features and mole_logs.
|
25
|
+
def mole_it(context, feature, user_id, args)
|
26
|
+
log_it( context, MoleFeature::find_feature( feature, ::Mole.application, context.class.name ), user_id, args ) if ::Mole::moleable?
|
27
|
+
end
|
28
|
+
|
29
|
+
# mole the bastard - create db entry into mole logs
|
30
|
+
def log_it( context, feature, user_id, args )
|
31
|
+
args ||= "no args"
|
32
|
+
user_id ||= "N/A"
|
33
|
+
ip_addr, browser_type = log_details( context )
|
34
|
+
MoleLog.create( :mole_feature => feature,
|
35
|
+
:user_id => user_id,
|
36
|
+
:host_name => `hostname`,
|
37
|
+
:params => args.to_yaml,
|
38
|
+
:ip_address => ip_addr,
|
39
|
+
:browser_type => browser_type )
|
40
|
+
end
|
41
|
+
|
42
|
+
# dumps partial stack
|
43
|
+
def dump_stack( boom )
|
44
|
+
buff = boom.backtrace[0...3].join( "-" )
|
45
|
+
end
|
46
|
+
|
47
|
+
# extract orginating ip address and browser type
|
48
|
+
def log_details( context ) #:nodoc:
|
49
|
+
ip_addr, browser_type = nil
|
50
|
+
if context.respond_to? :request
|
51
|
+
ip_addr, browser_type = context.request.env['REMOTE_ADDR'], context.request.env['HTTP_USER_AGENT']
|
52
|
+
end
|
53
|
+
return ip_addr, browser_type
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
data/lib/mole/module.rb
ADDED
@@ -0,0 +1,274 @@
|
|
1
|
+
# =============================================================================
|
2
|
+
# Extends module to inject interceptors
|
3
|
+
# =============================================================================
|
4
|
+
require 'benchmark'
|
5
|
+
require 'active_support'
|
6
|
+
|
7
|
+
class Module
|
8
|
+
# intercepts a collection of features and wrap performance check based on the
|
9
|
+
# specified :perf_threshold. The trigger defaults to 5 secs if not explicitly set.
|
10
|
+
#
|
11
|
+
# Example:
|
12
|
+
#
|
13
|
+
# MyClass.mole_perf do |context, action, elapsed_time, args|
|
14
|
+
# Mole::DbMole.perf_it( context.session[:user_id],
|
15
|
+
# :controller => context.class.name,
|
16
|
+
# :action => action,
|
17
|
+
# :elapsed_time => "%3.3f" % elapsed_time )
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# This will trap all public methods on the MyClass that takes more than
|
21
|
+
# :perf_threshold to complete. You can override this default by using the option
|
22
|
+
# :features => [m1,m2,...]. This is handy for controller moling rails
|
23
|
+
# and merb context.
|
24
|
+
#
|
25
|
+
# If you elect not to use the block form of the call, you can pass in the
|
26
|
+
# following arguments to the option hash:
|
27
|
+
# <tt>:interceptor</tt>:: The class name of your interceptor class
|
28
|
+
# <tt>:method</tt>:: The name of the method to callback the interceptor on
|
29
|
+
# once a perf condition has been trapped.
|
30
|
+
def mole_perf( opts={}, &filter )
|
31
|
+
opts[:interceptor] ||= filter
|
32
|
+
opts[:method] ||= :call
|
33
|
+
opts[:features] ||= instance_methods( false )
|
34
|
+
opts[:features].each do |feature|
|
35
|
+
wrap feature
|
36
|
+
perf_mole_filters[feature.to_s] << [opts[:interceptor], opts[:method]]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# monitors a collections of features and wrap rescue logic to trap unchecked
|
41
|
+
# exceptions. You can handle to trap differently by either logging the event
|
42
|
+
# in the db or sending out email/IM notification.
|
43
|
+
#
|
44
|
+
# Example:
|
45
|
+
#
|
46
|
+
# MyClass.mole_unchecked do |context, action, boom, args|
|
47
|
+
# Mole::Moler.check_it( context.session[:user_id],
|
48
|
+
# :controller => context.class.name,
|
49
|
+
# :action => action,
|
50
|
+
# :boom => boom )
|
51
|
+
# end
|
52
|
+
#
|
53
|
+
# This will wrap all public instance methods on MyClass. If any of these methods
|
54
|
+
# raises an unchecked exception, the MOle will surface the condition.
|
55
|
+
# This call also takes in a :features option to specify a list of methods if the
|
56
|
+
# default instance methods is not suitable, you can pass in a collection of methods
|
57
|
+
# that you wish to mole. This is handy in the case of Rails/Merb where conveniences
|
58
|
+
# are provided to gather controller actions
|
59
|
+
#
|
60
|
+
# If you elect not to use the block form of the call, you can pass in the
|
61
|
+
# following arguments to the option hash:
|
62
|
+
# <tt>:interceptor</tt>:: The class name of your interceptor class
|
63
|
+
# <tt>:method</tt>:: The name of the method to callback the interceptor on
|
64
|
+
# once an exception condition has been trapped.
|
65
|
+
def mole_unchecked( opts={}, &filter )
|
66
|
+
opts[:interceptor] ||= filter
|
67
|
+
opts[:method] ||= :call
|
68
|
+
opts[:features] ||= instance_methods( false )
|
69
|
+
opts[:features].each do |feature|
|
70
|
+
wrap feature
|
71
|
+
unchecked_mole_filters[feature.to_s] << [opts[:interceptor], opts[:method]]
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# intercepts a feature before the feature is called. During the callback
|
76
|
+
# you will have access to the object ( context ) on which the call is intercepted,
|
77
|
+
# as well as the arguments/block, the feature was issued with.
|
78
|
+
#
|
79
|
+
# Example:
|
80
|
+
#
|
81
|
+
# MyClass.mole_before( :feature => :blee ) do |context, feature, *args|
|
82
|
+
# Mole::Moler.mole_it( context, feature, context.session[:user_id],
|
83
|
+
# :args => args )
|
84
|
+
# end
|
85
|
+
#
|
86
|
+
# This will wrap the method blee with a before interceptor. Before the blee call
|
87
|
+
# is issued on MyClass, control will be handed to the before interceptor.
|
88
|
+
#
|
89
|
+
# Options:
|
90
|
+
# <tt>:feature</tt>:: The name of the feature to be intercepted
|
91
|
+
# If you elect not to use the block form of the call, you can pass in the
|
92
|
+
# following arguments to the option hash:
|
93
|
+
# <tt>:interceptor</tt>:: The class name of your interceptor class. If no interceptor block is specified
|
94
|
+
# <tt>:method</tt>:: The name of the method to callback the interceptor on
|
95
|
+
# once an exception condition has been trapped.
|
96
|
+
def mole_before(opts = {}, &filter)
|
97
|
+
raise "Missing :feature option" if opts[:feature].nil? or opts[:feature].to_s.empty?
|
98
|
+
opts[:interceptor] ||= filter
|
99
|
+
opts[:method] ||= :call
|
100
|
+
feature = opts[:feature].to_s
|
101
|
+
if before_mole_filters[feature].empty?
|
102
|
+
wrap feature
|
103
|
+
before_mole_filters[feature] << [opts[:interceptor], opts[:method]]
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
# intercepts a feature after the feature is called. During the callback
|
108
|
+
# you will have access to the object ( context ) on which the call is intercepted,
|
109
|
+
# as well as the arguments/block and return values the feature was issued with.
|
110
|
+
#
|
111
|
+
# Example:
|
112
|
+
#
|
113
|
+
# MyClass.mole_after( :feature => :blee ) do |context, feature, ret_val, *args|
|
114
|
+
# Mole::Moler.mole_it( context, feature, context.session[:user_id],
|
115
|
+
# :args => args )
|
116
|
+
# end
|
117
|
+
#
|
118
|
+
# This will wrap the method blee with an after interceptor. After the blee call
|
119
|
+
# is issued on MyClass, control will be handed to the after interceptor.
|
120
|
+
#
|
121
|
+
# Options:
|
122
|
+
# <tt>:feature</tt>:: The name of the feature to be intercepted
|
123
|
+
# <tt>:interceptor</tt>:: The class name of your interceptor class. If no interceptor block is specified
|
124
|
+
# <tt>:method</tt>:: The name of the method to callback the interceptor on
|
125
|
+
# once an exception condition has been trapped.
|
126
|
+
def mole_after(opts = {}, &interceptor)
|
127
|
+
raise "Missing :feature option" if opts[:feature].nil? or opts[:feature].to_s.empty?
|
128
|
+
opts[:interceptor] ||= interceptor
|
129
|
+
opts[:method] ||= :call
|
130
|
+
feature = opts[:feature].to_s
|
131
|
+
if after_mole_filters[feature].empty?
|
132
|
+
wrap feature
|
133
|
+
after_mole_filters[feature] << [opts[:interceptor], opts[:method]]
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
# def dump
|
138
|
+
# puts "Filters for class <- #{self} ->"
|
139
|
+
# puts "Before filters"
|
140
|
+
# before_mole_filters.each_pair do |k,v|
|
141
|
+
# puts "#{k} --> #{v}"
|
142
|
+
# end
|
143
|
+
# puts "After filters"
|
144
|
+
# after_mole_filters.each_pair do |k,v|
|
145
|
+
# puts "#{k} --> #{v}"
|
146
|
+
# end
|
147
|
+
# puts "Unchecked filters"
|
148
|
+
# unchecked_mole_filters.each_pair do |k,v|
|
149
|
+
# puts "#{k} --> #{v}"
|
150
|
+
# end
|
151
|
+
# puts "Perf filters"
|
152
|
+
# perf_mole_filters.each_pair do |k,v|
|
153
|
+
# puts "#{k} --> #{v}"
|
154
|
+
# end
|
155
|
+
# end
|
156
|
+
|
157
|
+
# ===========================================================================
|
158
|
+
# protected
|
159
|
+
|
160
|
+
# ---------------------------------------------------------------------------
|
161
|
+
# Holds before filters
|
162
|
+
def before_mole_filters #:nodoc:
|
163
|
+
@before_mole_filters ||= Hash.new{ |h,k| h[k] = [] }
|
164
|
+
end
|
165
|
+
|
166
|
+
# ---------------------------------------------------------------------------
|
167
|
+
# Holds after filters
|
168
|
+
def after_mole_filters #:nodoc:
|
169
|
+
@after_mole_filters ||= Hash.new{ |h,k| h[k] = [] }
|
170
|
+
end
|
171
|
+
|
172
|
+
# Holds perf around filters
|
173
|
+
def perf_mole_filters #:nodoc:
|
174
|
+
@perf_mole_filters ||= Hash.new{ |h,k| h[k] = []}
|
175
|
+
end
|
176
|
+
|
177
|
+
# Holds unchecked exception filters
|
178
|
+
def unchecked_mole_filters #:nodoc:
|
179
|
+
@unchecked_mole_filters ||= Hash.new{ |h,k| h[k] = []}
|
180
|
+
end
|
181
|
+
|
182
|
+
# Attempt to find singleton class method with given name
|
183
|
+
# TODO Figure out how to get method for static signature...
|
184
|
+
# def find_public_class_method(method)
|
185
|
+
# singleton_methods.each { |name| puts "Looking for #{method}--#{method.class} -- #{name}#{name.class}";return name if name == method }
|
186
|
+
# nil
|
187
|
+
# end
|
188
|
+
|
189
|
+
# Wrap method call
|
190
|
+
# TODO Add support for wrapping class methods ??
|
191
|
+
def wrap( method ) #:nodoc:
|
192
|
+
return if wrapped?( method )
|
193
|
+
begin
|
194
|
+
between = instance_method( method )
|
195
|
+
rescue
|
196
|
+
# between = find_public_class_method( method )
|
197
|
+
raise NameError unless(between)
|
198
|
+
end
|
199
|
+
code = <<-code
|
200
|
+
def #{method}_with_mole (*a, &b)
|
201
|
+
key = '#{method}'
|
202
|
+
klass = self.class
|
203
|
+
between = klass.wrapped[key]
|
204
|
+
ret_val = nil
|
205
|
+
klass.apply_before_filters( klass.before_mole_filters[key], self, key, *a, &b )
|
206
|
+
begin
|
207
|
+
elapsed = Benchmark::realtime do
|
208
|
+
ret_val = between.bind(self).call(*a)
|
209
|
+
end
|
210
|
+
klass.apply_perf_filters( elapsed, klass.perf_mole_filters[key], self, key, *a, &b )
|
211
|
+
rescue => boom
|
212
|
+
klass.apply_unchecked_filters( boom, klass.unchecked_mole_filters[key], self, key, *a, &b )
|
213
|
+
raise boom
|
214
|
+
end
|
215
|
+
klass.apply_after_filters( klass.after_mole_filters[key], self, key, *a, &b )
|
216
|
+
ret_val
|
217
|
+
end
|
218
|
+
code
|
219
|
+
|
220
|
+
module_eval code
|
221
|
+
alias_method_chain method, "mole"
|
222
|
+
wrapped[method.to_s] = between
|
223
|
+
end
|
224
|
+
|
225
|
+
def apply_before_filters( filters, clazz, key, *a, &b ) #:nodoc:
|
226
|
+
begin
|
227
|
+
filters.each { |r,m| r.send(m, clazz, key, *a, &b) }
|
228
|
+
rescue => ca_boom
|
229
|
+
::Mole.logger.error ">>> Mole Error: Before-Filter -- " + ca_boom
|
230
|
+
# ca_boom.backtrace.each { |l| ::Mole.logger.error l }
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
def apply_after_filters( filters, clazz, key, *a, &b ) #:nodoc:
|
235
|
+
begin
|
236
|
+
filters.each { |r,m| r.send(m, clazz, key, *a, &b) }
|
237
|
+
rescue => ca_boom
|
238
|
+
::Mole.logger.error ">>> Mole Error: After-Filter -- " + ca_boom
|
239
|
+
# ca_boom.backtrace.each { |l| ::Mole.logger.error l }
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
def apply_perf_filters( elapsed, filters, clazz, key, *a ) #:nodoc:
|
244
|
+
begin
|
245
|
+
if ( elapsed >= Mole.perf_threshold )
|
246
|
+
filters.each { |r,m| r.send(m, clazz, key, elapsed, *a) }
|
247
|
+
end
|
248
|
+
rescue => ca_boom
|
249
|
+
::Mole.logger.error ">>> Mole Error: Perf-Filter -- " + ca_boom
|
250
|
+
# ca_boom.backtrace.each { |l| ::Mole.logger.error l }
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
def apply_unchecked_filters( boom, filters, clazz, key, *a, &b ) #:nodoc:
|
255
|
+
begin
|
256
|
+
filters.each { |r,m| r.send(m, clazz, key, boom, *a, &b ) }
|
257
|
+
rescue => ca_boom
|
258
|
+
::Mole.logger.error ">>> Mole Error: Unchecked-Filter -- " + ca_boom
|
259
|
+
# ca_boom.backtrace.each { |l| ::Mole.logger.error l }
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
# ---------------------------------------------------------------------------
|
264
|
+
# Log wrapped class
|
265
|
+
def wrapped #:nodoc:
|
266
|
+
@wrapped ||= {}
|
267
|
+
end
|
268
|
+
|
269
|
+
# ---------------------------------------------------------------------------
|
270
|
+
# Check if method has been wrapped
|
271
|
+
def wrapped?(which) #:nodoc:
|
272
|
+
wrapped.has_key?(which.to_s)
|
273
|
+
end
|
274
|
+
end
|
data/lib/mole/moler.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
# -----------------------------------------------------------------------------
|
2
|
+
# Manages different ways to log the Mole interceptors. Currently there are two
|
3
|
+
# allowed modes ie persistent and transient
|
4
|
+
# Persistent mode will log the interception to the database for easy viewing
|
5
|
+
# and reporting.
|
6
|
+
# Transient mode will log the interaction to the mole logger.
|
7
|
+
# -----------------------------------------------------------------------------
|
8
|
+
module Mole
|
9
|
+
class Moler
|
10
|
+
class << self
|
11
|
+
# log an unchecked exception. Moler will look at the MOle
|
12
|
+
# configuration to see if this event should be persisted to the db or
|
13
|
+
# sent out to the logger.
|
14
|
+
def check_it( context, user_id, args )
|
15
|
+
return unless ::Mole.moleable?
|
16
|
+
if ::Mole.persistent?
|
17
|
+
MoleLog.log_it( context, MoleFeature::find_exception_feature( ::Mole.application ), user_id, args )
|
18
|
+
else
|
19
|
+
::Mole.logger.log_it( context, "Exception", user_id, args )
|
20
|
+
end
|
21
|
+
# Send out email notification if requested
|
22
|
+
Mole::EMole.deliver_exception_alerts( context, user_id, args ) if args[:email] and args[:email] == true
|
23
|
+
end
|
24
|
+
|
25
|
+
# log performance occurence.
|
26
|
+
def perf_it( context, user_id, args )
|
27
|
+
return unless ::Mole.moleable?
|
28
|
+
if ::Mole.persistent?
|
29
|
+
MoleLog.log_it( context, MoleFeature::find_performance_feature( ::Mole.application ), user_id, args )
|
30
|
+
else
|
31
|
+
::Mole.logger.log_it( context, "Performance", user_id, args )
|
32
|
+
end
|
33
|
+
# Send out email notification if requested
|
34
|
+
Mole::EMole.deliver_perf_alerts( context, user_id, args ) if args[:email] and args[:email] == true
|
35
|
+
end
|
36
|
+
|
37
|
+
# log a mole feature occurence.
|
38
|
+
def mole_it(context, feature, user_id, args)
|
39
|
+
return unless ::Mole.moleable?
|
40
|
+
if ::Mole.persistent?
|
41
|
+
MoleLog.log_it( context, MoleFeature::find_feature( feature, ::Mole.application, context.class.name ), user_id, args )
|
42
|
+
else
|
43
|
+
::Mole.logger.log_it( context, feature, user_id, args )
|
44
|
+
end
|
45
|
+
# Send out email notification if requested
|
46
|
+
args[:feature] = feature
|
47
|
+
Mole::EMole.deliver_feature_alerts( context, user_id, args ) if args[:email] and args[:email] == true
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# -----------------------------------------------------------------------------
|
2
|
+
# Convenience for extracting actions out controller classes
|
3
|
+
# Currently supported frameworks : Rails and Merb, more in the future...
|
4
|
+
# -----------------------------------------------------------------------------
|
5
|
+
module Mole
|
6
|
+
module Utils
|
7
|
+
class Frameworks
|
8
|
+
class << self
|
9
|
+
# Retrieves the collection of callable actions from a Merb controller.
|
10
|
+
def merb_actions( controller_class )
|
11
|
+
actions = []
|
12
|
+
controller_class.send( :callable_actions ).keys.each { |action| actions << action }
|
13
|
+
end
|
14
|
+
|
15
|
+
# Retrieves the collection of callable actions from a Rails controller
|
16
|
+
def rails_actions( controller_class )
|
17
|
+
controller_class.send( :action_methods )
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|