rackamole 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/README.txt +93 -0
- data/Rakefile +28 -0
- data/aaa.txt +36 -0
- data/bin/rackamole +8 -0
- data/images/mole_logo.png +0 -0
- data/images/mole_logo.psd +0 -0
- data/images/mole_logo_small.png +0 -0
- data/images/mole_logo_small.psd +0 -0
- data/lib/rackamole.rb +46 -0
- data/lib/rackamole/interceptor.rb +20 -0
- data/lib/rackamole/logger.rb +121 -0
- data/lib/rackamole/mole.rb +134 -0
- data/lib/rackamole/store/log.rb +55 -0
- data/lib/rackamole/store/mongo.rb +104 -0
- data/samples/rails/moled/README +243 -0
- data/samples/rails/moled/Rakefile +10 -0
- data/samples/rails/moled/app/controllers/application_controller.rb +13 -0
- data/samples/rails/moled/app/controllers/fred_controller.rb +4 -0
- data/samples/rails/moled/app/helpers/application_helper.rb +3 -0
- data/samples/rails/moled/app/views/fred/index.html.erb +1 -0
- data/samples/rails/moled/config/boot.rb +110 -0
- data/samples/rails/moled/config/database.yml +22 -0
- data/samples/rails/moled/config/environment.rb +45 -0
- data/samples/rails/moled/config/environments/development.rb +17 -0
- data/samples/rails/moled/config/environments/production.rb +28 -0
- data/samples/rails/moled/config/environments/test.rb +28 -0
- data/samples/rails/moled/config/initializers/backtrace_silencers.rb +7 -0
- data/samples/rails/moled/config/initializers/inflections.rb +10 -0
- data/samples/rails/moled/config/initializers/mime_types.rb +5 -0
- data/samples/rails/moled/config/initializers/new_rails_defaults.rb +19 -0
- data/samples/rails/moled/config/initializers/session_store.rb +15 -0
- data/samples/rails/moled/config/locales/en.yml +5 -0
- data/samples/rails/moled/config/routes.rb +43 -0
- data/samples/rails/moled/db/development.sqlite3 +0 -0
- data/samples/rails/moled/doc/README_FOR_APP +2 -0
- data/samples/rails/moled/log/development.log +30 -0
- data/samples/rails/moled/log/production.log +0 -0
- data/samples/rails/moled/log/server.log +0 -0
- data/samples/rails/moled/log/test.log +0 -0
- data/samples/rails/moled/public/404.html +30 -0
- data/samples/rails/moled/public/422.html +30 -0
- data/samples/rails/moled/public/500.html +30 -0
- data/samples/rails/moled/public/favicon.ico +0 -0
- data/samples/rails/moled/public/images/rails.png +0 -0
- data/samples/rails/moled/public/index.html +275 -0
- data/samples/rails/moled/public/javascripts/application.js +2 -0
- data/samples/rails/moled/public/javascripts/controls.js +963 -0
- data/samples/rails/moled/public/javascripts/dragdrop.js +973 -0
- data/samples/rails/moled/public/javascripts/effects.js +1128 -0
- data/samples/rails/moled/public/javascripts/prototype.js +4320 -0
- data/samples/rails/moled/public/robots.txt +5 -0
- data/samples/rails/moled/script/about +4 -0
- data/samples/rails/moled/script/console +3 -0
- data/samples/rails/moled/script/dbconsole +3 -0
- data/samples/rails/moled/script/destroy +3 -0
- data/samples/rails/moled/script/generate +3 -0
- data/samples/rails/moled/script/performance/benchmarker +3 -0
- data/samples/rails/moled/script/performance/profiler +3 -0
- data/samples/rails/moled/script/plugin +3 -0
- data/samples/rails/moled/script/runner +3 -0
- data/samples/rails/moled/script/server +3 -0
- data/samples/rails/moled/test/performance/browsing_test.rb +9 -0
- data/samples/rails/moled/test/test_helper.rb +38 -0
- data/samples/sinatra/moled.rb +20 -0
- data/spec/expected_results/mole_exception.log +17 -0
- data/spec/expected_results/mole_feature.log +15 -0
- data/spec/expected_results/mole_perf.log +16 -0
- data/spec/rackamole/interceptor_spec.rb +33 -0
- data/spec/rackamole/logger_spec.rb +55 -0
- data/spec/rackamole/mole_spec.rb +90 -0
- data/spec/rackamole/store/log_spec.rb +54 -0
- data/spec/rackamole/store/mongo_spec.rb +120 -0
- data/spec/rackamole_spec.rb +20 -0
- data/spec/spec_helper.rb +8 -0
- data/tasks/ann.rake +80 -0
- data/tasks/bones.rake +20 -0
- data/tasks/gem.rake +201 -0
- data/tasks/git.rake +40 -0
- data/tasks/notes.rake +27 -0
- data/tasks/post_load.rake +34 -0
- data/tasks/rdoc.rake +51 -0
- data/tasks/rubyforge.rake +55 -0
- data/tasks/setup.rb +292 -0
- data/tasks/spec.rake +54 -0
- data/tasks/svn.rake +47 -0
- data/tasks/test.rake +40 -0
- data/tasks/zentest.rake +36 -0
- metadata +198 -0
data/History.txt
ADDED
data/README.txt
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
== RackAMole
|
2
|
+
Your web application observer...
|
3
|
+
|
4
|
+
== DESCRIPTION:
|
5
|
+
|
6
|
+
The MOle is a rack application that monitors user interactions with your web site. We are not
|
7
|
+
talking about counting page hits here. The MOle tracks all the information available to capture
|
8
|
+
the essence of a user interaction with your application. Using the MOle, you are able to see
|
9
|
+
which feature is a hit or a bust. As an added bonus, the MOle also track performance and exceptions
|
10
|
+
that might have escaped your test suites or alpha env. To boot your managers will love you for it!
|
11
|
+
|
12
|
+
Whether you are releasing a new application or improving on an old one, it is always a good thing
|
13
|
+
to know if anyone is using your application and if they are, how they are using it.
|
14
|
+
What features are your users most fond of and which features find their way into the abyss?
|
15
|
+
You will be able to rapidly assess whether or not your application is a hit and if
|
16
|
+
your coolest features are thought as such by your users. You will be able to elegantly record user
|
17
|
+
interactions and leverage these findings for the next iteration of your application.
|
18
|
+
|
19
|
+
== PROJECT INFORMATION
|
20
|
+
|
21
|
+
* Developer: Fernand Galiana [liquidrail.com]
|
22
|
+
* Forum: http://groups.google.com/group/rackamole
|
23
|
+
* Git: git://github.com/derailed/rackamole.git
|
24
|
+
|
25
|
+
== FEATURES:
|
26
|
+
|
27
|
+
* Monitors Rails or Sinatra applications
|
28
|
+
* Captures the essence of the request as well as user information
|
29
|
+
* Tracks performance issues based on your latency threshold
|
30
|
+
* Tracks exceptions that might occurred during a request
|
31
|
+
|
32
|
+
== REQUIREMENTS:
|
33
|
+
|
34
|
+
* Logging
|
35
|
+
* Hitimes
|
36
|
+
* MongoDb
|
37
|
+
|
38
|
+
== INSTALL:
|
39
|
+
|
40
|
+
* sudo gem install rackamole
|
41
|
+
|
42
|
+
NOTE: The gem is hosted on gemcutter.com - please update your gem sources if not
|
43
|
+
already specified
|
44
|
+
|
45
|
+
== USAGE:
|
46
|
+
|
47
|
+
=== Rails applications
|
48
|
+
|
49
|
+
Edit your environment.rb file and add the following lines:
|
50
|
+
|
51
|
+
require 'rackamole'
|
52
|
+
config.middleware.use Rack::Mole, { :app_name => "My Cool App", :user_key => :user_name }
|
53
|
+
|
54
|
+
This instructs the mole to start logging information to the console and look for the user name
|
55
|
+
in the session using the :user_name key. There are other options available, please take a look
|
56
|
+
at the docs for more information.
|
57
|
+
|
58
|
+
=== Sinatra Applications
|
59
|
+
|
60
|
+
Add the following lines in the config section and smoke it...
|
61
|
+
|
62
|
+
require 'rackamole'
|
63
|
+
configure do
|
64
|
+
use Rack::Mole, { :app_name => "My Sinatra App", :user_key => :user_name }
|
65
|
+
end
|
66
|
+
|
67
|
+
This assumes that you have session enabled to identify a user if not the mole will log the user
|
68
|
+
as 'Unknown'
|
69
|
+
|
70
|
+
== LICENSE:
|
71
|
+
|
72
|
+
(The MIT License)
|
73
|
+
|
74
|
+
Copyright (c) 2008 FIXME (different license?)
|
75
|
+
|
76
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
77
|
+
a copy of this software and associated documentation files (the
|
78
|
+
'Software'), to deal in the Software without restriction, including
|
79
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
80
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
81
|
+
permit persons to whom the Software is furnished to do so, subject to
|
82
|
+
the following conditions:
|
83
|
+
|
84
|
+
The above copyright notice and this permission notice shall be
|
85
|
+
included in all copies or substantial portions of the Software.
|
86
|
+
|
87
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
88
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
89
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
90
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
91
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
92
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
93
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
begin
|
2
|
+
require 'bones'
|
3
|
+
Bones.setup
|
4
|
+
rescue LoadError
|
5
|
+
begin
|
6
|
+
load 'tasks/setup.rb'
|
7
|
+
rescue LoadError
|
8
|
+
raise RuntimeError, '### please install the "bones" gem ###'
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
ensure_in_path 'lib'
|
13
|
+
require 'rackamole'
|
14
|
+
|
15
|
+
task :default => 'spec:run'
|
16
|
+
|
17
|
+
PROJ.name = 'rackamole'
|
18
|
+
PROJ.authors = 'Fernand Galiana'
|
19
|
+
PROJ.email = 'fernand.galiana@gmail.com'
|
20
|
+
PROJ.url = 'http://rackamole.liquidrail.com'
|
21
|
+
PROJ.version = Rackamole::VERSION
|
22
|
+
PROJ.spec.opts << '--color'
|
23
|
+
PROJ.ruby_opts = %w[-W0]
|
24
|
+
|
25
|
+
# Dependencies
|
26
|
+
depend_on "logging" , ">= 1.2.2"
|
27
|
+
depend_on "hitimes" , ">= 1.0.3"
|
28
|
+
depend_on "mongodb-mongo", ">= 0.14.1"
|
data/aaa.txt
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
== Problem
|
2
|
+
|
3
|
+
All tests green, CC happy, Customers are creaming their pants reading their Cucumber scenarios. App is in production on bitch'n servers.
|
4
|
+
Top of the world right?
|
5
|
+
|
6
|
+
== Questions
|
7
|
+
|
8
|
+
Is the app being used ? How many customers hit it in the past week ? What are the most/least used features ? Why?
|
9
|
+
Are my cool ajax callbacks silently crapping out? Are there any perf issues ? ... ?
|
10
|
+
|
11
|
+
== Got Mole ? (http://github.com/derailed/rackamole)
|
12
|
+
|
13
|
+
The MOle is a rack component that traps your application requests and captures the associated context information.
|
14
|
+
By intercepting the request, this rack app can gather calling context, alert you of latency issues on certain operation and
|
15
|
+
also trap any exceptions that may have emerged in the request cycle. The MOle is currently able to either store this information
|
16
|
+
in your log files or in a mongo instance for later processing.
|
17
|
+
|
18
|
+
== Options
|
19
|
+
|
20
|
+
<tt>:app_name</tt> - The name of the application [Default: Moled App]
|
21
|
+
<tt>:environment</tt> - The environment for the application ie :environment => RAILS_ENV
|
22
|
+
<tt>:moleable</tt> - Enable/Disable the MOle [Default:true]
|
23
|
+
<tt>:store </tt> - The storage instance ie log file or database [Default:stdout]
|
24
|
+
<tt>:user_key</tt> - If session is enable, the session key for the user name or user_id. ie :user_key => :user_name
|
25
|
+
|
26
|
+
== Rails
|
27
|
+
|
28
|
+
require 'rackamole'
|
29
|
+
config.middleware.use Rack::Mole, { :app_name => "My Cool App", :user_key => :user_name }
|
30
|
+
|
31
|
+
== Sinatra
|
32
|
+
|
33
|
+
require 'rackamole'
|
34
|
+
configure do
|
35
|
+
use Rack::Mole, { :app_name => "My Sinatra App", :user_key => :user_name }
|
36
|
+
end
|
data/bin/rackamole
ADDED
Binary file
|
Binary file
|
Binary file
|
Binary file
|
data/lib/rackamole.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
module Rackamole
|
2
|
+
|
3
|
+
# :stopdoc:
|
4
|
+
VERSION = '0.0.1'
|
5
|
+
LIBPATH = ::File.expand_path(::File.dirname(__FILE__)) + ::File::SEPARATOR
|
6
|
+
PATH = ::File.dirname(LIBPATH) + ::File::SEPARATOR
|
7
|
+
# :startdoc:
|
8
|
+
|
9
|
+
# Returns the version string for the library.
|
10
|
+
#
|
11
|
+
def self.version
|
12
|
+
VERSION
|
13
|
+
end
|
14
|
+
|
15
|
+
# Returns the library path for the module. If any arguments are given,
|
16
|
+
# they will be joined to the end of the libray path using
|
17
|
+
# <tt>File.join</tt>.
|
18
|
+
#
|
19
|
+
def self.libpath( *args )
|
20
|
+
args.empty? ? LIBPATH : ::File.join(LIBPATH, args.flatten)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Returns the lpath for the module. If any arguments are given,
|
24
|
+
# they will be joined to the end of the path using
|
25
|
+
# <tt>File.join</tt>.
|
26
|
+
#
|
27
|
+
def self.path( *args )
|
28
|
+
args.empty? ? PATH : ::File.join(PATH, args.flatten)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Utility method used to require all files ending in .rb that lie in the
|
32
|
+
# directory below this file that has the same name as the filename passed
|
33
|
+
# in. Optionally, a specific _directory_ name can be passed in such that
|
34
|
+
# the _filename_ does not have to be equivalent to the directory.
|
35
|
+
#
|
36
|
+
def self.require_all_libs_relative_to( fname, dir = nil )
|
37
|
+
dir ||= ::File.basename(fname, '.*')
|
38
|
+
search_me = ::File.expand_path(
|
39
|
+
::File.join(::File.dirname(fname), dir, '**', '*.rb'))
|
40
|
+
|
41
|
+
Dir.glob(search_me).sort.each {|rb| require rb}
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
Rackamole.require_all_libs_relative_to(__FILE__)
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Rackamole
|
2
|
+
module Interceptor
|
3
|
+
|
4
|
+
# In order for the mole to trap framework exception, you must include
|
5
|
+
# the interceptor in your application controller.
|
6
|
+
# ie include Wackamole::Interceptor
|
7
|
+
def self.included( base )
|
8
|
+
base.send( :alias_method_chain, :rescue_action_in_public, :mole )
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
# Instructs the mole to trap the framework exception
|
14
|
+
def rescue_action_in_public_with_mole( exception )
|
15
|
+
# Stuff the exception in the env for mole rack retrieval
|
16
|
+
request.env['mole.exception'] = exception
|
17
|
+
rescue_action_in_public_without_mole( exception )
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
require 'logging'
|
2
|
+
require 'forwardable'
|
3
|
+
|
4
|
+
module Rackamole
|
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 => nil ,
|
46
|
+
:email_alert_buffsize => 200 ,
|
47
|
+
}
|
48
|
+
end
|
49
|
+
|
50
|
+
# create a new logger
|
51
|
+
#
|
52
|
+
def initialize( opts = {} )
|
53
|
+
@options = ::Rackamole::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
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
require 'hitimes'
|
2
|
+
require 'mongo/util/ordered_hash'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module Rack
|
6
|
+
class Mole
|
7
|
+
|
8
|
+
# Initialize The Mole with the possible options
|
9
|
+
# <tt>:app_name</tt> - The name of the application [Default: Moled App]
|
10
|
+
# <tt>:environment</tt> - The environment for the application ie :environment => RAILS_ENV
|
11
|
+
# <tt>:perf_threshold</tt> - Any request taking longer than this value will get moled [Default: 10]
|
12
|
+
# <tt>:moleable</tt> - Enable/Disable the MOle [Default:true]
|
13
|
+
# <tt>:store</tt> - The storage instance ie log file or mongodb [Default:stdout]
|
14
|
+
# <tt>:user_key</tt> - If session is enable, the session key for the user name or user_id. ie :user_key => :user_name
|
15
|
+
def initialize( app, opts={} )
|
16
|
+
@app = app
|
17
|
+
init_options( opts )
|
18
|
+
end
|
19
|
+
|
20
|
+
def call( env )
|
21
|
+
# Bail if application is not moleable
|
22
|
+
return @app.call( env ) unless moleable?
|
23
|
+
|
24
|
+
response = nil
|
25
|
+
elapsed = Hitimes::Interval.measure do
|
26
|
+
response = @app.call( env )
|
27
|
+
end
|
28
|
+
@store.mole( mole_info( env, elapsed ) )
|
29
|
+
response
|
30
|
+
end
|
31
|
+
|
32
|
+
# ===========================================================================
|
33
|
+
private
|
34
|
+
|
35
|
+
# Load up configuration options
|
36
|
+
def init_options( opts )
|
37
|
+
options = default_options.merge( opts )
|
38
|
+
@environment = options[:environment]
|
39
|
+
@perf_threshold = options[:perf_threshold]
|
40
|
+
@moleable = options[:moleable]
|
41
|
+
@app_name = options[:app_name]
|
42
|
+
@user_key = options[:user_key]
|
43
|
+
@store = options[:store]
|
44
|
+
end
|
45
|
+
|
46
|
+
# Mole default options
|
47
|
+
def default_options
|
48
|
+
{
|
49
|
+
:app_name => "Moled App",
|
50
|
+
:moleable => true,
|
51
|
+
:perf_threshold => 10,
|
52
|
+
:store => Rackamole::Store::Log.new
|
53
|
+
}
|
54
|
+
end
|
55
|
+
|
56
|
+
# Extract interesting information from the request
|
57
|
+
def mole_info( env, elapsed )
|
58
|
+
request = Rack::Request.new( env )
|
59
|
+
session = env['rack.session']
|
60
|
+
route = get_route( request )
|
61
|
+
info = OrderedHash.new
|
62
|
+
ip, browser = identify( env )
|
63
|
+
user_id = nil
|
64
|
+
user_name = nil
|
65
|
+
|
66
|
+
if session and @user_key
|
67
|
+
if @user_key.instance_of? Symbol
|
68
|
+
user_name = session[@user_key]
|
69
|
+
elsif @user_key.instance_of? Hash
|
70
|
+
user_id = session[ @user_key[:session_key] ]
|
71
|
+
if @user_key[:extractor]
|
72
|
+
user_name = @user_key[:extractor].call( user_id )
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
info[:app_name] = @app_name
|
78
|
+
info[:environment] = @environment if @environment
|
79
|
+
info[:user_id] = user_id if user_id
|
80
|
+
info[:user_name] = user_name || "Unknown"
|
81
|
+
info[:ip] = ip
|
82
|
+
info[:browser] = browser
|
83
|
+
info[:request_time] = elapsed if elapsed
|
84
|
+
info[:perf_issue] = (elapsed and elapsed > @perf_threshold)
|
85
|
+
info[:url] = request.url
|
86
|
+
info[:method] = env['REQUEST_METHOD']
|
87
|
+
info[:path] = request.path
|
88
|
+
info[:route_info] = route if route
|
89
|
+
|
90
|
+
# Dump request params
|
91
|
+
unless request.params.empty?
|
92
|
+
info[:params] = OrderedHash.new
|
93
|
+
request.params.keys.sort.each { |k| info[:params][k.to_sym] = request.params[k].to_json }
|
94
|
+
end
|
95
|
+
|
96
|
+
# Dump session var
|
97
|
+
if session and !session.empty?
|
98
|
+
info[:session] = OrderedHash.new
|
99
|
+
session.keys.sort{ |a,b| a.to_s <=> b.to_s }.each { |k| info[:session][k.to_sym] = session[k].to_json }
|
100
|
+
end
|
101
|
+
|
102
|
+
# Check if an exception was raised. If so consume it and clear state
|
103
|
+
exception = env['mole.exception']
|
104
|
+
if exception
|
105
|
+
info[:ruby_version] = %x[ruby -v]
|
106
|
+
info[:stack] = trim_stack( exception )
|
107
|
+
env['mole.exception'] = nil
|
108
|
+
end
|
109
|
+
|
110
|
+
info
|
111
|
+
end
|
112
|
+
|
113
|
+
# Trim stack trace
|
114
|
+
def trim_stack( boom )
|
115
|
+
boom.backtrace[0...4]
|
116
|
+
end
|
117
|
+
|
118
|
+
# Identify request ie ip and browser configuration
|
119
|
+
def identify( request_env )
|
120
|
+
return request_env['HTTP_X_FORWARDED_FOR'] || request_env['REMOTE_ADDR'], request_env['HTTP_USER_AGENT']
|
121
|
+
end
|
122
|
+
|
123
|
+
# Checks if this application is moleable
|
124
|
+
def moleable?
|
125
|
+
@moleable
|
126
|
+
end
|
127
|
+
|
128
|
+
# Fetch route info if any...
|
129
|
+
def get_route( request )
|
130
|
+
return nil unless defined?( RAILS_ENV )
|
131
|
+
::ActionController::Routing::Routes.recognize_path( request.path, {:method => request.request_method } )
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|