crashlog 0.0.1 → 0.0.2

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