crashlog 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. data/.gitignore +2 -1
  2. data/.rspec +1 -0
  3. data/.travis.yml +11 -0
  4. data/Gemfile +10 -0
  5. data/Gemfile.lock +161 -0
  6. data/INSTALL +22 -0
  7. data/README.md +14 -4
  8. data/Rakefile +13 -0
  9. data/crashlog.gemspec +12 -3
  10. data/generators/crashlog/templates/initializer.rb +6 -0
  11. data/install.rb +2 -0
  12. data/lib/crash_log/backtrace/line.rb +105 -0
  13. data/lib/crash_log/backtrace/line_cache.rb +23 -0
  14. data/lib/crash_log/backtrace.rb +66 -0
  15. data/lib/crash_log/configuration.bak.rb +199 -0
  16. data/lib/crash_log/configuration.rb +188 -0
  17. data/lib/crash_log/logging.rb +51 -0
  18. data/lib/crash_log/payload.rb +157 -0
  19. data/lib/crash_log/rack.rb +47 -0
  20. data/lib/crash_log/rails/action_controller_rescue.rb +32 -0
  21. data/lib/crash_log/rails/controller_methods.rb +45 -0
  22. data/lib/crash_log/rails/middleware/debug_exception_catcher.rb +43 -0
  23. data/lib/crash_log/rails.rb +32 -0
  24. data/lib/crash_log/railtie.rb +41 -0
  25. data/lib/crash_log/reporter.rb +105 -0
  26. data/lib/crash_log/system_information.rb +64 -0
  27. data/lib/crash_log/templates/payload.rabl +7 -0
  28. data/lib/crash_log/version.rb +1 -1
  29. data/lib/crash_log.rb +118 -0
  30. data/lib/faraday/request/hmac_authentication.rb +73 -0
  31. data/lib/rails/generators/crashlog/crashlog_generator.rb +42 -0
  32. data/rails/init.rb +1 -0
  33. data/spec/crash_log/backtrace_spec.rb +79 -0
  34. data/spec/crash_log/initializer_spec.rb +53 -0
  35. data/spec/crash_log/payload_spec.rb +124 -0
  36. data/spec/crash_log/reporter_spec.rb +179 -0
  37. data/spec/crash_log_spec.rb +153 -0
  38. data/spec/dummy/README.rdoc +261 -0
  39. data/spec/dummy/Rakefile +7 -0
  40. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  41. data/spec/dummy/app/assets/stylesheets/application.css +13 -0
  42. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  43. data/spec/dummy/app/controllers/break_controller.rb +10 -0
  44. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  45. data/spec/dummy/app/mailers/.gitkeep +0 -0
  46. data/spec/dummy/app/models/.gitkeep +0 -0
  47. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  48. data/spec/dummy/config/application.rb +59 -0
  49. data/spec/dummy/config/boot.rb +10 -0
  50. data/spec/dummy/config/database.yml +44 -0
  51. data/spec/dummy/config/environment.rb +5 -0
  52. data/spec/dummy/config/environments/development.rb +37 -0
  53. data/spec/dummy/config/environments/production.rb +67 -0
  54. data/spec/dummy/config/environments/test.rb +37 -0
  55. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  56. data/spec/dummy/config/initializers/crashlog.rb +6 -0
  57. data/spec/dummy/config/initializers/inflections.rb +15 -0
  58. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  59. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  60. data/spec/dummy/config/initializers/session_store.rb +8 -0
  61. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  62. data/spec/dummy/config/routes.rb +6 -0
  63. data/spec/dummy/config.ru +4 -0
  64. data/spec/dummy/lib/assets/.gitkeep +0 -0
  65. data/spec/dummy/log/.gitkeep +0 -0
  66. data/spec/dummy/public/404.html +26 -0
  67. data/spec/dummy/public/422.html +26 -0
  68. data/spec/dummy/public/500.html +25 -0
  69. data/spec/dummy/public/favicon.ico +0 -0
  70. data/spec/dummy/script/rails +6 -0
  71. data/spec/requests/rack_spec.rb +29 -0
  72. data/spec/requests/rails_controller_rescue_spec.rb +46 -0
  73. data/spec/spec_helper.rb +23 -0
  74. data/spec/support/doing.rb +1 -0
  75. data/spec/support/dummy_app.rb +13 -0
  76. data/spec/support/hash_ext.rb +7 -0
  77. metadata +197 -7
@@ -0,0 +1,199 @@
1
+ module CrashLog
2
+ class Configuration
3
+
4
+ DEFAULT_PARAMS_FILTERS = %w(password password_confirmation).freeze
5
+
6
+ DEFAULT_BACKTRACE_FILTERS = [
7
+ lambda { |line|
8
+ if defined?(CrashLog.configuration.project_root) &&
9
+ CrashLog.configuration.project_root.to_s != ''
10
+ line.sub(/#{CrashLog.configuration.project_root}/, "[PROJECT_ROOT]")
11
+ else
12
+ line
13
+ end
14
+ },
15
+ lambda { |line| line.gsub(/^\.\//, "") },
16
+ lambda { |line|
17
+ if defined?(Gem)
18
+ Gem.path.inject(line) do |line, path|
19
+ line.gsub(/#{path}/, "[GEM_ROOT]")
20
+ end
21
+ end
22
+ },
23
+ lambda { |line| line if line !~ %r{lib/crash_log} }
24
+ ].freeze
25
+
26
+ ENVIRONMENT_FILTERS = []
27
+
28
+ IGNORE_DEFAULT = ['ActiveRecord::RecordNotFound',
29
+ 'ActionController::RoutingError',
30
+ 'ActionController::InvalidAuthenticityToken',
31
+ 'CGI::Session::CookieStore::TamperedWithCookie',
32
+ 'ActionController::UnknownAction',
33
+ 'AbstractController::ActionNotFound',
34
+ 'Mongoid::Errors::DocumentNotFound']
35
+
36
+ # The default logging device
37
+ #
38
+ # This will be set to Rails.logger automatically if using Rails,
39
+ # otherwise it defaults to STDOUT.
40
+ attr_accessor :logger
41
+
42
+ # The API key to authenticate this project with CrashLog
43
+ #
44
+ # Get this from your projects configuration page within http://CrashLog.io
45
+ attr_accessor :api_key
46
+
47
+ # Stages (environments) which we consider to be in a production environment
48
+ # and thus you want to be sent notifications for.
49
+ attr_accessor :release_stages
50
+
51
+ # The name of the current stage
52
+ attr_reader :stage
53
+
54
+ # Project Root directory
55
+ attr_accessor :project_root
56
+ alias :root :project_root
57
+ alias :root= :project_root=
58
+
59
+ # If set, this will serialize the object returned by sending this key to
60
+ # the controller context. You can use this to send user data CrashLog to
61
+ # correlate errors with users to give you more advanced data about directly
62
+ # who was affected.
63
+ #
64
+ # All user data is stored encrypted for security and always remains your
65
+ # property.
66
+ attr_accessor :user_context_key
67
+
68
+ # Reporter configuration options
69
+ #
70
+ # Host to send exceptions to. Default: crashlog.io
71
+ attr_accessor :host
72
+
73
+ # Port to use for http connections. 80 or 443 by default.
74
+ attr_writer :port
75
+
76
+ def port
77
+ if @port
78
+ @port
79
+ else
80
+ secure? ? 443 : 80
81
+ end
82
+ end
83
+
84
+ # HTTP transfer scheme, default: https
85
+ attr_accessor :scheme
86
+
87
+ # API endpoint to context for notifications. Default /notify
88
+ attr_accessor :endpoint
89
+
90
+ # The faraday adapter to use to make the connection.
91
+ #
92
+ # Possible values are:
93
+ # - :test
94
+ # - :net_http
95
+ # - :net_http_persistent
96
+ # - :typhoeus
97
+ # - :patron
98
+ # - :em_synchrony
99
+ # - :em_http
100
+ # - :excon
101
+ # - :rack
102
+ # - :httpclient
103
+ #
104
+ # Possible performance gains can be made by using the em based adapters if
105
+ # your application supports this architecture.
106
+ #
107
+ # Default: net_http
108
+ attr_accessor :adapter
109
+
110
+ # Reader for Array of ignored error class names
111
+ attr_reader :ignore
112
+
113
+ attr_reader :announce_endpoint, :announce
114
+
115
+ # Send context lines for backtrace.
116
+ #
117
+ # Takes an integer of the number of lines, set to false or nil to disable.
118
+ attr_accessor :context_lines
119
+
120
+ # Environment variables to discard from ENV.
121
+ attr_accessor :environment_filters
122
+
123
+ # Framework name
124
+ attr_accessor :framework
125
+
126
+ attr_accessor :dry_run
127
+
128
+ attr_accessor :http_read_timeout, :http_open_timeout
129
+
130
+ def initialize
131
+ @dry_run = false
132
+ @secure = true
133
+ @use_system_ssl_cert_chain= false
134
+ @host = 'crashlog.io'
135
+ @http_open_timeout = 5
136
+ @http_read_timeout = 2
137
+ @adapter = :net_http
138
+ @params_filters = DEFAULT_PARAMS_FILTERS.dup
139
+ @backtrace_filters = DEFAULT_BACKTRACE_FILTERS.dup
140
+ @environment_filters = ENVIRONMENT_FILTERS.dup
141
+ @ignore_by_filters = []
142
+ @ignore = IGNORE_DEFAULT.dup
143
+ @release_stages = %w(production staging)
144
+ @notifier_version = CrashLog::VERSION
145
+ @notifier_url = 'https://github.com/ivanvanderbyl/crashlog'
146
+ @framework = 'Standalone'
147
+ @stage = 'development'
148
+ @host = 'stdin.crashlog.io'
149
+ @port = nil
150
+ @scheme = port.eql?(443) ? 'https' : 'http'
151
+ @endpoint = '/notify'
152
+ @announce = true
153
+ @announce_endpoint = '/announce'
154
+ @context_lines = 5
155
+ @framework = 'Standalone'
156
+ end
157
+
158
+ def release_stage?
159
+ @release_stages.include?(stage)
160
+ end
161
+
162
+ def stage=(name)
163
+ @stage = name.downcase.strip
164
+ end
165
+
166
+ # Is this configuration valid for sending exceptions to CrashLog
167
+ #
168
+ # Returns true if all required keys are provided, otherwise false
169
+ def valid?
170
+ [:api_key, :host, :port].all? do |key|
171
+ !__send__(key).nil?
172
+ end
173
+ end
174
+
175
+ def ignored?(exception)
176
+ ignore.include?(error_class(exception))
177
+ end
178
+
179
+ def secure?
180
+ scheme == 'https'
181
+ end
182
+
183
+ # Hash like accessor
184
+ def [](key)
185
+ if self.respond_to?(key)
186
+ self.__send__(key)
187
+ end
188
+ end
189
+
190
+ private
191
+
192
+ def error_class(exception)
193
+ # The "Class" check is for some strange exceptions like Timeout::Error
194
+ # which throw the error class instead of an instance
195
+ (exception.is_a? Class) ? exception.name : exception.class.name
196
+ end
197
+
198
+ end
199
+ end
@@ -0,0 +1,188 @@
1
+ require 'hashr'
2
+ module CrashLog
3
+ class Configuration < Hashr
4
+ DEFAULT_PARAMS_FILTERS = %w(password password_confirmation).freeze
5
+
6
+ DEFAULT_BACKTRACE_FILTERS = [
7
+ lambda { |line|
8
+ if defined?(CrashLog.configuration.root) &&
9
+ CrashLog.configuration.root.to_s != ''
10
+ line.sub(/#{CrashLog.configuration.root}/, "[PROJECT_ROOT]")
11
+ else
12
+ line
13
+ end
14
+ },
15
+ lambda { |line| line.gsub(/^\.\//, "") },
16
+ lambda { |line|
17
+ if defined?(Gem)
18
+ Gem.path.inject(line) do |line, path|
19
+ line.gsub(/#{path}/, "[GEM_ROOT]")
20
+ end
21
+ end
22
+ },
23
+ lambda { |line| line if line !~ %r{lib/crash_log} }
24
+ ].freeze
25
+
26
+ IGNORE_DEFAULT = ['ActiveRecord::RecordNotFound',
27
+ 'ActionController::RoutingError',
28
+ 'ActionController::InvalidAuthenticityToken',
29
+ 'CGI::Session::CookieStore::TamperedWithCookie',
30
+ 'ActionController::UnknownAction',
31
+ 'AbstractController::ActionNotFound',
32
+ 'Mongoid::Errors::DocumentNotFound']
33
+
34
+
35
+ # The logger to use for internal messages
36
+ define :logger => nil,
37
+
38
+ # The API key to authenticate this project with CrashLog
39
+ #
40
+ # Get this from your projects configuration page within http://CrashLog.io
41
+ :api_key => nil,
42
+ :project_id => nil,
43
+
44
+ # Stages (environments) which we consider to be in a production environment
45
+ # and thus you want to be sent notifications for.
46
+ :release_stages => ['staging', 'production'],
47
+
48
+ # The name of the current stage
49
+ :stage => 'development',
50
+
51
+ # Project Root directory
52
+ :project_root => nil,
53
+
54
+ # If set, this will serialize the object returned by sending this key to
55
+ # the controller context. You can use this to send user data CrashLog to
56
+ # correlate errors with users to give you more advanced data about directly
57
+ # who was affected.
58
+ #
59
+ # All user data is stored encrypted for security and always remains your
60
+ # property.
61
+ :user_context_key => nil,
62
+
63
+ # Reporter configuration options
64
+ #
65
+ # Host to send exceptions to. Default: crashlog.io
66
+ :host => 'stdin.crashlog.io',
67
+
68
+ # Port to use for http connections. 80 or 443 by default.
69
+ # :port => 443,
70
+
71
+ # HTTP transfer scheme, default: https
72
+ :scheme => 'https',
73
+
74
+ # API endpoint to context for notifications. Default /events
75
+ :endpoint => '/events',
76
+
77
+ # The faraday adapter to use to make the connection.
78
+ #
79
+ # Possible values are:
80
+ # - :test
81
+ # - :net_http
82
+ # - :net_http_persistent
83
+ # - :typhoeus
84
+ # - :patron
85
+ # - :em_synchrony
86
+ # - :em_http
87
+ # - :excon
88
+ # - :rack
89
+ # - :httpclient
90
+ #
91
+ # Possible performance gains can be made by using the em based adapters if
92
+ # your application supports this architecture.
93
+ #
94
+ # Default: net_http
95
+ :adapter => :net_http,
96
+
97
+ # Timeout for actually sending the exeption payload
98
+ :http_read_timeout => 2,
99
+
100
+ # Timeout for connecting to CrashLog collector interface
101
+ :http_open_timeout => 5,
102
+
103
+ # Ignored error class names
104
+ :ignore => IGNORE_DEFAULT.dup,
105
+
106
+ # Endpoint used for announcing application launch
107
+ :announce_endpoint => '/announce',
108
+ :announce => true,
109
+
110
+ # Send context lines for backtrace.
111
+ #
112
+ # Takes an integer of the number of lines, set to nil to disable.
113
+ :context_lines => 5,
114
+
115
+ # Environment variables to discard from ENV.
116
+ :environment_filters => [],
117
+
118
+ # Framework name
119
+ :framework => 'Standalone',
120
+
121
+ # Run in dry mode (Doesn't actually send exceptions, used for testing)
122
+ :dry_run => false,
123
+
124
+ :backtrace_filters => DEFAULT_BACKTRACE_FILTERS.dup,
125
+
126
+ :params_filters => DEFAULT_PARAMS_FILTERS.dup,
127
+
128
+ # Internal
129
+ # Do not change unless you know what this does.
130
+ :service_name => 'CrashLog'
131
+
132
+ def root
133
+ fetch(:project_root)
134
+ end
135
+
136
+ def root=(string)
137
+ self[:project_root] = string
138
+ end
139
+
140
+ def port
141
+ if secure?
142
+ 443
143
+ else
144
+ fetch(:port, 80)
145
+ end
146
+ end
147
+
148
+ # Release stages are stages which send exceptions
149
+ def release_stage?
150
+ release_stages.include?(stage)
151
+ end
152
+
153
+ # Set the current stage
154
+ def stage=(name)
155
+ self[:stage] = name.downcase.strip
156
+ end
157
+
158
+ # Is this configuration valid for sending exceptions to CrashLog
159
+ #
160
+ # Returns true if all required keys are provided, otherwise false
161
+ def valid?
162
+ [:api_key, :project_id, :host, :port].all? do |key|
163
+ !__send__(key).nil?
164
+ end
165
+ end
166
+
167
+ def ignored?(exception)
168
+ ignore.include?(error_class(exception))
169
+ end
170
+
171
+ def secure?
172
+ fetch(:scheme, 'https') == 'https'
173
+ end
174
+
175
+ def notifier_version
176
+ CrashLog::VERSION
177
+ end
178
+
179
+ private
180
+
181
+ def error_class(exception)
182
+ # The "Class" check is for some strange exceptions like Timeout::Error
183
+ # which throw the error class instead of an instance
184
+ (exception.is_a? Class) ? exception.name : exception.class.name
185
+ end
186
+
187
+ end
188
+ end
@@ -0,0 +1,51 @@
1
+ require 'active_support/core_ext/module/delegation'
2
+ require 'active_support/core_ext/module/aliasing'
3
+ require 'logger'
4
+
5
+ module CrashLog
6
+ module Logging
7
+ ANSI = {
8
+ :red => 31,
9
+ :green => 32,
10
+ :yellow => 33,
11
+ :cyan => 36
12
+ }
13
+
14
+ def self.included(base)
15
+ base.__send__(:include, ClassMethods)
16
+ end
17
+
18
+ module ClassMethods
19
+ delegate :logger, :to => CrashLog
20
+
21
+ [:fatal, :error, :warn, :info, :debug].each do |level|
22
+ define_method(level) do |*args|
23
+ message, options = *args
24
+ message.chomp.split("\n").each do |line|
25
+ logger.send(level, prefix(line))
26
+ end
27
+ end
28
+ end
29
+
30
+ def prefix(string)
31
+ [CrashLog::LOG_PREFIX, string].join(' ')
32
+ end
33
+
34
+ def colorize(color, text)
35
+ "\e[#{ANSI[color]}m#{text}\e[0m"
36
+ end
37
+
38
+ def log_exception(exception)
39
+ logger.error("#{exception.class.name}: #{exception.message}")
40
+ exception.backtrace.each { |line| logger.error(line) } if exception.backtrace
41
+ rescue Exception => e
42
+ puts '--- FATAL ---'
43
+ puts 'an exception occured while logging an exception'
44
+ puts e.message, e.backtrace
45
+ puts exception.message, exception.backtrace
46
+ end
47
+
48
+ end
49
+
50
+ end
51
+ end
@@ -0,0 +1,157 @@
1
+ require 'rabl'
2
+
3
+ module CrashLog
4
+ class Payload
5
+ include Logging
6
+
7
+ def self.build(exception, config, &block)
8
+ payload = new(exception, config)
9
+ yield(payload) if block_given?
10
+ payload
11
+ end
12
+
13
+ # Delivers this payload to CrashLog
14
+ #
15
+ # Captures any exceptions and logs them.
16
+ def deliver!
17
+ deliver
18
+ rescue Exception => e
19
+ error('Failed to deliver notification to CrashLog collector')
20
+ log_exception(e)
21
+ end
22
+
23
+ attr_reader :config, :backtrace_filters
24
+
25
+ def initialize(event_data, config)
26
+ @config = config || {}
27
+ @event_data = event_data
28
+ @context = {}
29
+ @environment = {}
30
+ @backtrace_filters = config[:backtrace_filters] || []
31
+
32
+ # Actually serialize the exception/event hash for transport
33
+ @event = serialize_event(event_data)
34
+
35
+ # add_environment_data(:system => SystemInformation.to_hash)
36
+ end
37
+
38
+ def deliver
39
+ Reporter.new(config).notify(self.body)
40
+ end
41
+
42
+ attr_reader :event, :backtrace, :exception_object, :environment, :context
43
+
44
+ def body
45
+ renderer.render
46
+ end
47
+
48
+ def add_context(data)
49
+ (@context ||= {}).merge!(data) if data.is_a?(Hash)
50
+ end
51
+
52
+ def add_user_data(data, value = nil)
53
+ if data.respond_to?(:keys)
54
+ @user_data.merge!(data)
55
+ elsif value && data.respond_to?(:to_sym)
56
+ @user_data[data.to_sym] = value
57
+ end
58
+ end
59
+
60
+ def add_session_data(data)
61
+ (@environment[:session] ||= {}).merge!(data) if data.is_a?(Hash)
62
+ end
63
+
64
+ def add_environment_data(data)
65
+ @environment.merge!(data) if data.respond_to?(:keys)
66
+ end
67
+
68
+ # The canonical time this exception occurred.
69
+ #
70
+ # Other notifiers leave this to the collector to set, we however take time
71
+ # more seriously and use this figure internally to detect processing time
72
+ # irregularities.
73
+ #
74
+ # Returns UNIX UTC timestamp integer.
75
+ def timestamp
76
+ Time.now.utc.to_i
77
+ end
78
+
79
+ # Various meta data about this notifier gem
80
+ def notifier
81
+ {
82
+ :name => "crashlog",
83
+ :version => CrashLog::VERSION
84
+ }
85
+ end
86
+
87
+ # Returns the hostname of this machine
88
+ def hostname
89
+ SystemInformation.hostname
90
+ end
91
+
92
+ private
93
+
94
+ def serialize_event(event_data)
95
+ if event_data.is_a?(Exception)
96
+ @backtrace = build_backtrace(event_data)
97
+ serialize_exception(event_data)
98
+
99
+ elsif event_data.is_a?(Hash)
100
+ event_data.merge(:timestamp => timestamp)
101
+
102
+ elsif event_data.respond_to?(:message) && event_data.respond_to?(:type)
103
+ ducktype_event(event_data)
104
+
105
+ end
106
+ end
107
+
108
+ def ducktype_event(event_data)
109
+ {}.tap do |response|
110
+ response[:timestamp] = timestamp
111
+ response[:type] = event_data.type
112
+ response[:message] = event_data.message
113
+ end
114
+ end
115
+
116
+ def serialize_exception(exception)
117
+ exception = unwrap_exception(exception)
118
+ # opts = opts.merge(:exception => exception) if exception.is_a?(Exception)
119
+ # opts = opts.merge(exception.to_hash) if exception.respond_to?(:to_hash)
120
+
121
+ {}.tap do |response|
122
+ response[:timestamp] = timestamp
123
+ response[:message] = exception.message
124
+ response[:type] = error_class(exception)
125
+ end
126
+ end
127
+
128
+ def build_backtrace(exception)
129
+ Backtrace.parse((exception.backtrace || caller),
130
+ :filters => @backtrace_filters).to_a
131
+ end
132
+
133
+ def error_class(exception)
134
+ # The "Class" check is for some strange exceptions like Timeout::Error
135
+ # which throw the error class instead of an instance
136
+ (exception.is_a? Class) ? exception.name : exception.class.name
137
+ end
138
+
139
+ def unwrap_exception(exception)
140
+ if exception.respond_to?(:original_exception)
141
+ exception.original_exception
142
+ elsif exception.respond_to?(:continued_exception)
143
+ exception.continued_exception
144
+ else
145
+ exception
146
+ end
147
+ end
148
+
149
+ def renderer
150
+ Rabl::Renderer.new('payload', self, { :format => 'hash', :view_path => view_path })
151
+ end
152
+
153
+ def view_path
154
+ File.expand_path('../templates', __FILE__)
155
+ end
156
+ end
157
+ end
@@ -0,0 +1,47 @@
1
+ module CrashLog
2
+ # CrashLog Middleware for Rack applications. Any errors raised by the upstream
3
+ # application will be delivered to CrashLog and re-raised.
4
+ #
5
+ # Synopsis:
6
+ #
7
+ # require 'rack'
8
+ # require 'crashlog'
9
+ #
10
+ # CrashLog.configure do |config|
11
+ # config.api_key = 'project-api-key'
12
+ # end
13
+ #
14
+ # app = Rack::Builder.app do
15
+ # run lambda { |env| raise "Fully Racked" }
16
+ # end
17
+ #
18
+ # use CrashLog::Rack
19
+ # run app
20
+ #
21
+ # Use a standard CrashLog.configure call to configure your api key.
22
+
23
+ class Rack
24
+ def initialize(app)
25
+ @app = app
26
+ CrashLog.configuration.logger ||= Logger.new($stdout)
27
+ end
28
+
29
+ def call(env)
30
+ begin
31
+ response = @app.call(env)
32
+ rescue Exception => exception
33
+ error_id = CrashLog.notify(exception, :rack_env => env)
34
+ env['crash_log.error_id'] = error_id
35
+ raise
36
+ end
37
+
38
+ if env['rack.exception']
39
+ error_id = CrashLog.notify(env['rack.exception'], :rack_env => env)
40
+ env['crash_log.error_id'] = error_id
41
+ end
42
+
43
+ response
44
+ end
45
+ end
46
+ end
47
+
@@ -0,0 +1,32 @@
1
+ module CrashLog
2
+ module Rails
3
+
4
+ # CrashLog controller integration
5
+ # Aliases method chain for Rails internal rescue action
6
+ module ActionControllerRescue
7
+ def self.included(base)
8
+ base.send(:alias_method, :rescue_action_in_public_without_crash_log, :rescue_action_in_public)
9
+ base.send(:alias_method, :rescue_action_in_public, :rescue_action_in_public_with_crash_log)
10
+
11
+ base.send(:alias_method, :rescue_action_locally_without_crash_log, :rescue_action_locally)
12
+ base.send(:alias_method, :rescue_action_locally, :rescue_action_locally_with_crash_log)
13
+ end
14
+
15
+ private
16
+
17
+ # crash_log_context is defined in controller_methods.rb
18
+ def rescue_action_in_public_with_crash_log(exception)
19
+ crash_log.auto_notify(exception, crash_log_context)
20
+ rescue_action_in_public_without_crash_log(exception)
21
+ end
22
+
23
+ # crash_log_context is defined in controller_methods.rb
24
+ def rescue_action_locally_with_crash_log(exception)
25
+ crash_log.auto_notify(exception, crash_log_context)
26
+ rescue_action_locally_without_crash_log(exception)
27
+ end
28
+ end
29
+ end
30
+
31
+ end
32
+