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.
- data/.gitignore +2 -1
- data/.rspec +1 -0
- data/.travis.yml +11 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +161 -0
- data/INSTALL +22 -0
- data/README.md +14 -4
- data/Rakefile +13 -0
- data/crashlog.gemspec +12 -3
- data/generators/crashlog/templates/initializer.rb +6 -0
- data/install.rb +2 -0
- data/lib/crash_log/backtrace/line.rb +105 -0
- data/lib/crash_log/backtrace/line_cache.rb +23 -0
- data/lib/crash_log/backtrace.rb +66 -0
- data/lib/crash_log/configuration.bak.rb +199 -0
- data/lib/crash_log/configuration.rb +188 -0
- data/lib/crash_log/logging.rb +51 -0
- data/lib/crash_log/payload.rb +157 -0
- data/lib/crash_log/rack.rb +47 -0
- data/lib/crash_log/rails/action_controller_rescue.rb +32 -0
- data/lib/crash_log/rails/controller_methods.rb +45 -0
- data/lib/crash_log/rails/middleware/debug_exception_catcher.rb +43 -0
- data/lib/crash_log/rails.rb +32 -0
- data/lib/crash_log/railtie.rb +41 -0
- data/lib/crash_log/reporter.rb +105 -0
- data/lib/crash_log/system_information.rb +64 -0
- data/lib/crash_log/templates/payload.rabl +7 -0
- data/lib/crash_log/version.rb +1 -1
- data/lib/crash_log.rb +118 -0
- data/lib/faraday/request/hmac_authentication.rb +73 -0
- data/lib/rails/generators/crashlog/crashlog_generator.rb +42 -0
- data/rails/init.rb +1 -0
- data/spec/crash_log/backtrace_spec.rb +79 -0
- data/spec/crash_log/initializer_spec.rb +53 -0
- data/spec/crash_log/payload_spec.rb +124 -0
- data/spec/crash_log/reporter_spec.rb +179 -0
- data/spec/crash_log_spec.rb +153 -0
- data/spec/dummy/README.rdoc +261 -0
- data/spec/dummy/Rakefile +7 -0
- data/spec/dummy/app/assets/javascripts/application.js +13 -0
- data/spec/dummy/app/assets/stylesheets/application.css +13 -0
- data/spec/dummy/app/controllers/application_controller.rb +3 -0
- data/spec/dummy/app/controllers/break_controller.rb +10 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/mailers/.gitkeep +0 -0
- data/spec/dummy/app/models/.gitkeep +0 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/config/application.rb +59 -0
- data/spec/dummy/config/boot.rb +10 -0
- data/spec/dummy/config/database.yml +44 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +37 -0
- data/spec/dummy/config/environments/production.rb +67 -0
- data/spec/dummy/config/environments/test.rb +37 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/crashlog.rb +6 -0
- data/spec/dummy/config/initializers/inflections.rb +15 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/secret_token.rb +7 -0
- data/spec/dummy/config/initializers/session_store.rb +8 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/routes.rb +6 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/lib/assets/.gitkeep +0 -0
- data/spec/dummy/log/.gitkeep +0 -0
- data/spec/dummy/public/404.html +26 -0
- data/spec/dummy/public/422.html +26 -0
- data/spec/dummy/public/500.html +25 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/script/rails +6 -0
- data/spec/requests/rack_spec.rb +29 -0
- data/spec/requests/rails_controller_rescue_spec.rb +46 -0
- data/spec/spec_helper.rb +23 -0
- data/spec/support/doing.rb +1 -0
- data/spec/support/dummy_app.rb +13 -0
- data/spec/support/hash_ext.rb +7 -0
- metadata +197 -7
@@ -0,0 +1,45 @@
|
|
1
|
+
module CrashLog
|
2
|
+
module Rails
|
3
|
+
module ControllerMethods
|
4
|
+
|
5
|
+
def crash_log_context
|
6
|
+
{ :parameters => crash_log_filter_if_filtering(params.to_hash),
|
7
|
+
:session_data => crash_log_filter_if_filtering(crash_log_session_data),
|
8
|
+
:controller => params[:controller],
|
9
|
+
:action => params[:action],
|
10
|
+
:url => crash_log_request_url,
|
11
|
+
:cgi_data => crash_log_filter_if_filtering(request.env)
|
12
|
+
}
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
def notify_crashlog(exception, custom_data = nil)
|
17
|
+
request_data = crash_log_context
|
18
|
+
#request_data[:meta_data][:custom] = custom_data if custom_data
|
19
|
+
CrashLog.notify(exception, request_data)
|
20
|
+
end
|
21
|
+
|
22
|
+
alias_method :notify_airbrake, :notify_crashlog
|
23
|
+
|
24
|
+
def crash_log_session_data
|
25
|
+
if session.respond_to?(:to_hash)
|
26
|
+
session.to_hash
|
27
|
+
else
|
28
|
+
session.data
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def crash_log_request_url
|
33
|
+
url = "#{request.protocol}#{request.host}"
|
34
|
+
|
35
|
+
unless [80, 443].include?(request.port)
|
36
|
+
url << ":#{request.port}"
|
37
|
+
end
|
38
|
+
|
39
|
+
url << request.fullpath
|
40
|
+
url
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module CrashLog
|
2
|
+
module Rails
|
3
|
+
module Middleware
|
4
|
+
module DebugExceptionCatcher
|
5
|
+
|
6
|
+
def self.included(base)
|
7
|
+
base.send(:alias_method_chain, :render_exception, :crash_log)
|
8
|
+
end
|
9
|
+
|
10
|
+
# Hook into the rails error rendering page to send this exception to
|
11
|
+
# CrashLog before rails handlers take over.
|
12
|
+
def render_exception_with_crash_log(env, exception)
|
13
|
+
controller = env['action_controller.instance']
|
14
|
+
|
15
|
+
env['crash_log.error_id'] = CrashLog.notify(exception) #,
|
16
|
+
# crash_log_context(controller, env))
|
17
|
+
|
18
|
+
if defined?(controller.rescue_action_in_public_without_crash_log)
|
19
|
+
controller.rescue_action_in_public_without_crash_log(exception)
|
20
|
+
end
|
21
|
+
|
22
|
+
rescue Exception => e
|
23
|
+
# If it breaks here there is possibly something wrong with us, so
|
24
|
+
# instead of crashing again, we'll just pass it on.
|
25
|
+
ensure
|
26
|
+
render_exception_without_crash_log(env, exception)
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def crash_log_context(controller, env)
|
32
|
+
# TODO: Replace this with user context lookup
|
33
|
+
if controller.respond_to?(:crash_log_request_data)
|
34
|
+
controller.crash_log_request_data
|
35
|
+
else
|
36
|
+
{:rack_env => env}
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require "crash_log"
|
2
|
+
require "crash_log/rails/controller_methods"
|
3
|
+
require "crash_log/rails/action_controller_rescue"
|
4
|
+
|
5
|
+
# Rails 2.x support
|
6
|
+
module CrashLog
|
7
|
+
module Rails
|
8
|
+
def self.initialize
|
9
|
+
if defined?(ActionController::Base)
|
10
|
+
ActionController::Base.send(:include, CrashLog::Rails::ActionControllerRescue)
|
11
|
+
ActionController::Base.send(:include, CrashLog::Rails::ControllerMethods)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Try to find where to log to
|
15
|
+
rails_logger = nil
|
16
|
+
if defined?(::Rails.logger)
|
17
|
+
rails_logger = ::Rails.logger
|
18
|
+
elsif defined?(RAILS_DEFAULT_LOGGER)
|
19
|
+
rails_logger = RAILS_DEFAULT_LOGGER
|
20
|
+
end
|
21
|
+
|
22
|
+
CrashLog.configure do |config|
|
23
|
+
config.logger = rails_logger
|
24
|
+
config.release_stage = RAILS_ENV if defined?(RAILS_ENV)
|
25
|
+
config.project_root = RAILS_ROOT if defined?(RAILS_ROOT)
|
26
|
+
config.framework = "Rails: #{::Rails::VERSION::STRING}" if defined?(::Rails::VERSION)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
CrashLog::Rails.initialize
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'crash_log'
|
2
|
+
require 'rails'
|
3
|
+
|
4
|
+
# Rails 3.x support
|
5
|
+
module CrashLog
|
6
|
+
class Railtie < ::Rails::Railtie
|
7
|
+
|
8
|
+
config.after_initialize do
|
9
|
+
CrashLog.configure do |config|
|
10
|
+
config.logger ||= ::Rails.logger
|
11
|
+
config.stage ||= ::Rails.env
|
12
|
+
config.project_root ||= ::Rails.root
|
13
|
+
config.framework = "Rails: #{::Rails::VERSION::STRING}"
|
14
|
+
end
|
15
|
+
|
16
|
+
initializer "crash_log.use_rack_middleware" do |app|
|
17
|
+
app.config.middleware.insert 0, "CrashLog::Rack"
|
18
|
+
end
|
19
|
+
|
20
|
+
# Attach our Rails Controller methods
|
21
|
+
ActiveSupport.on_load(:action_controller) do
|
22
|
+
# Lazily load action_controller methods
|
23
|
+
require 'crash_log/rails/controller_methods'
|
24
|
+
|
25
|
+
include CrashLog::Rails::ControllerMethods
|
26
|
+
end
|
27
|
+
|
28
|
+
if defined?(::ActionDispatch::DebugExceptions)
|
29
|
+
# We should catch the exceptions in ActionDispatch::DebugExceptions in Rails 3.2.x.
|
30
|
+
require 'crash_log/rails/middleware/debug_exception_catcher'
|
31
|
+
::ActionDispatch::DebugExceptions.__send__(:include, CrashLog::Rails::Middleware::DebugExceptionCatcher)
|
32
|
+
elsif defined?(::ActionDispatch::ShowExceptions)
|
33
|
+
|
34
|
+
# ActionDispatch::DebugExceptions is not defined in Rails 3.0.x and 3.1.x so
|
35
|
+
# catch the exceptions in ShowExceptions.
|
36
|
+
require 'crash_log/rails/middleware/debug_exception_catcher'
|
37
|
+
::ActionDispatch::ShowExceptions.send(:include, CrashLog::Rails::Middleware::DebugExceptionCatcher)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
require 'faraday/request/hmac_authentication'
|
3
|
+
require 'uuid'
|
4
|
+
require 'yajl'
|
5
|
+
|
6
|
+
module CrashLog
|
7
|
+
class Reporter
|
8
|
+
include Logging
|
9
|
+
|
10
|
+
attr_reader :host, :port, :scheme, :endpoint, :announce_endpoint
|
11
|
+
attr_reader :result, :config, :response
|
12
|
+
|
13
|
+
def initialize(config)
|
14
|
+
@config = config
|
15
|
+
@scheme = config.scheme
|
16
|
+
@host = config.host
|
17
|
+
@port = config.port
|
18
|
+
@endpoint = config.endpoint
|
19
|
+
@announce_endpoint = config.announce == true ?
|
20
|
+
config.announce_endpoint : nil
|
21
|
+
end
|
22
|
+
|
23
|
+
def notify(payload)
|
24
|
+
#return if dry_run?
|
25
|
+
#MultiJson.use(:yajl)
|
26
|
+
response = post(endpoint, MultiJson.encode({:payload => payload}))
|
27
|
+
@response = response
|
28
|
+
report_result(response.body)
|
29
|
+
response.success?
|
30
|
+
rescue => e
|
31
|
+
log_exception e
|
32
|
+
error("Sending exception failed due to a connectivity issue")
|
33
|
+
end
|
34
|
+
|
35
|
+
def announce
|
36
|
+
return if dry_run?
|
37
|
+
return "Unknown application" unless announce_endpoint
|
38
|
+
|
39
|
+
response = post(config.announce_endpoint, JSON.dump(identification_hash))
|
40
|
+
if response.status == 201
|
41
|
+
JSON.load(response.body).symbolize_keys.fetch(:application, 'Default')
|
42
|
+
else
|
43
|
+
false
|
44
|
+
end
|
45
|
+
rescue => e
|
46
|
+
# We only want to log our mess when testing
|
47
|
+
log_exception(e) # if respond_to?(:should)
|
48
|
+
error("Failed to announce application launch")
|
49
|
+
nil
|
50
|
+
end
|
51
|
+
|
52
|
+
def report_result(body)
|
53
|
+
@result = JSON.load(body).symbolize_keys
|
54
|
+
end
|
55
|
+
|
56
|
+
def url
|
57
|
+
URI.parse("#{scheme}://#{host}:#{port}")
|
58
|
+
end
|
59
|
+
|
60
|
+
def identification_hash
|
61
|
+
{
|
62
|
+
:hostname => SystemInformation.hostname,
|
63
|
+
:timestamp => Time.now.utc.to_i
|
64
|
+
}
|
65
|
+
end
|
66
|
+
|
67
|
+
def print_result
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
def dry_run?
|
72
|
+
config.dry_run == true
|
73
|
+
end
|
74
|
+
|
75
|
+
def post(endpoint, body)
|
76
|
+
connection.post do |req|
|
77
|
+
req.url(endpoint)
|
78
|
+
# req.sign!(config.project_id, config.api_key)
|
79
|
+
req.headers['Content-Type'] = 'application/json'
|
80
|
+
req.body = body
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# private
|
85
|
+
|
86
|
+
def connection
|
87
|
+
@connection ||= begin
|
88
|
+
Faraday.new(:url => url) do |faraday|
|
89
|
+
faraday.request :hmac_authentication, config.project_id, config.api_key, {:service_id => config.service_name}
|
90
|
+
faraday.adapter(adapter)
|
91
|
+
faraday.request :url_encoded
|
92
|
+
# faraday.request :token_auth, config.api_key
|
93
|
+
# faraday.response :logger
|
94
|
+
# faraday.options[:timeout] = config.http_read_timeout
|
95
|
+
# faraday.options[:open_timeout] = config.http_open_timeout
|
96
|
+
# faraday.ssl[:verify] = false
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def adapter
|
102
|
+
config.adapter
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module CrashLog
|
2
|
+
class SystemInformation
|
3
|
+
class << self
|
4
|
+
|
5
|
+
def to_hash
|
6
|
+
{
|
7
|
+
:hostname => hostname,
|
8
|
+
:ruby_version => ruby_version,
|
9
|
+
:username => username,
|
10
|
+
:environment => environment,
|
11
|
+
:libraries_loaded => libraries_loaded,
|
12
|
+
:platform => platform,
|
13
|
+
:application_root => application_root,
|
14
|
+
:stage => stage
|
15
|
+
}
|
16
|
+
end
|
17
|
+
|
18
|
+
def hostname
|
19
|
+
require 'socket' unless defined?(Socket)
|
20
|
+
Socket.gethostname
|
21
|
+
rescue
|
22
|
+
'UNKNOWN'
|
23
|
+
end
|
24
|
+
|
25
|
+
def ruby_version
|
26
|
+
"#{RUBY_VERSION rescue '?.?.?'}-p#{RUBY_PATCHLEVEL rescue '???'} #{RUBY_RELEASE_DATE rescue '????-??-??'} #{RUBY_PLATFORM rescue '????'}"
|
27
|
+
end
|
28
|
+
|
29
|
+
def username
|
30
|
+
ENV['LOGNAME'] || ENV['USER'] || ENV['USERNAME'] || ENV['APACHE_RUN_USER'] || 'UNKNOWN'
|
31
|
+
end
|
32
|
+
|
33
|
+
def environment
|
34
|
+
if ENV.respond_to?(:to_hash)
|
35
|
+
ENV.to_hash.reject do |k, v|
|
36
|
+
(k =~ /^HTTP_/) || CrashLog.configuration.environment_filters.include?(k)
|
37
|
+
end
|
38
|
+
else
|
39
|
+
{}
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def libraries_loaded
|
44
|
+
Hash[*Gem.loaded_specs.map {|name, gem_specification|
|
45
|
+
[name, gem_specification.version.to_s]
|
46
|
+
}.flatten]
|
47
|
+
rescue
|
48
|
+
{}
|
49
|
+
end
|
50
|
+
|
51
|
+
def platform
|
52
|
+
RUBY_PLATFORM rescue '????'
|
53
|
+
end
|
54
|
+
|
55
|
+
def application_root
|
56
|
+
CrashLog.configuration.root
|
57
|
+
end
|
58
|
+
|
59
|
+
def stage
|
60
|
+
CrashLog.configuration.stage
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
data/lib/crash_log/version.rb
CHANGED
data/lib/crash_log.rb
CHANGED
@@ -1,5 +1,123 @@
|
|
1
|
+
$: << File.expand_path('..', __FILE__)
|
2
|
+
|
1
3
|
require 'crash_log/version'
|
4
|
+
begin
|
5
|
+
require 'active_support'
|
6
|
+
require 'active_support/core_ext'
|
7
|
+
rescue LoadError
|
8
|
+
require 'activesupport'
|
9
|
+
require 'activesupport/core_ext'
|
10
|
+
end
|
11
|
+
require 'faraday'
|
12
|
+
require 'crash_log/railtie' if defined?(Rails::Railtie)
|
13
|
+
|
14
|
+
require 'crash_log/logging'
|
2
15
|
|
3
16
|
module CrashLog
|
17
|
+
extend Logging::ClassMethods
|
18
|
+
|
19
|
+
autoload :Backtrace, 'crash_log/backtrace'
|
20
|
+
autoload :Configuration, 'crash_log/configuration'
|
21
|
+
autoload :Payload, 'crash_log/payload'
|
22
|
+
autoload :Rack, 'crash_log/rack'
|
23
|
+
autoload :Reporter, 'crash_log/reporter'
|
24
|
+
autoload :SystemInformation, 'crash_log/system_information'
|
25
|
+
|
26
|
+
LOG_PREFIX = '** [CrashLog]'
|
27
|
+
|
28
|
+
class << self
|
29
|
+
|
30
|
+
# Sends a notification to CrashLog
|
31
|
+
#
|
32
|
+
# This is the main entry point into the exception sending stack.
|
33
|
+
#
|
34
|
+
# Examples:
|
35
|
+
#
|
36
|
+
# def something_dangerous
|
37
|
+
# raise RuntimeError, "This is too dangerous for you"
|
38
|
+
# rescue => e
|
39
|
+
# CrashLog.notify(e)
|
40
|
+
# end
|
41
|
+
#
|
42
|
+
# You can also include information about the current user and Crashlog
|
43
|
+
# will allow you to correlate errors by affected users:
|
44
|
+
#
|
45
|
+
# def something_dangerous
|
46
|
+
# raise RuntimeError, "This is too dangerous for you"
|
47
|
+
# rescue => e
|
48
|
+
# CrashLog.notify(e, {current_user: current_user})
|
49
|
+
# end
|
50
|
+
#
|
51
|
+
# This will try to serialize the current user by calling `as_crashlog_context` or `as_json`
|
52
|
+
# otherwise it will try `to_s`
|
53
|
+
#
|
54
|
+
# Returns true if successful, otherwise false
|
55
|
+
def notify(exception, context = {})
|
56
|
+
send_notification(exception, context)
|
57
|
+
end
|
58
|
+
|
59
|
+
# Sends the notice unless it is one of the default ignored exceptions.
|
60
|
+
def notify_or_ignore(exception, context = {})
|
61
|
+
send_notification(exception, context = {}) unless ignored?(exception)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Print a message at the top of the applciation's logs to say we're ready.
|
65
|
+
def report_for_duty!
|
66
|
+
application = CrashLog::Reporter.new(configuration).announce
|
67
|
+
|
68
|
+
if application
|
69
|
+
info("Initialized and ready to handle exceptions for #{application}")
|
70
|
+
else
|
71
|
+
error("Failed to report for duty, your application failed to authenticate correctly with stdin.crashlog.io")
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# Configure the gem to send notifications, at the very least an api_key is
|
76
|
+
# required.
|
77
|
+
def configure
|
78
|
+
yield(configuration) if block_given?
|
79
|
+
if configuration.valid?
|
80
|
+
report_for_duty!
|
81
|
+
else
|
82
|
+
error('Not configured correctly')
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# The global configuration object.
|
87
|
+
def configuration
|
88
|
+
@configuration ||= Configuration.new
|
89
|
+
end
|
90
|
+
|
91
|
+
# The default logging device.
|
92
|
+
def logger
|
93
|
+
self.configuration.logger || Logger.new($stdout)
|
94
|
+
end
|
95
|
+
|
96
|
+
# Is the logger live
|
97
|
+
#
|
98
|
+
# Returns true if the current stage is included in the release
|
99
|
+
# stages config, false otherwise.
|
100
|
+
def live?
|
101
|
+
configuration.release_stage?
|
102
|
+
end
|
103
|
+
|
104
|
+
# Looks up ignored exceptions
|
105
|
+
#
|
106
|
+
# Returns true if this exception should be ignored, false otherwise.
|
107
|
+
def ignored?(exception)
|
108
|
+
configuration.ignored?(exception)
|
109
|
+
end
|
110
|
+
|
111
|
+
private
|
112
|
+
|
113
|
+
def send_notification(exception, context = {})
|
114
|
+
build_payload(exception, context).deliver! if live?
|
115
|
+
end
|
4
116
|
|
117
|
+
def build_payload(exception, context = {})
|
118
|
+
Payload.build(exception, configuration) do |payload|
|
119
|
+
payload.add_context(context)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
5
123
|
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module Faraday
|
2
|
+
class Request
|
3
|
+
|
4
|
+
require 'crash_log/auth_hmac'
|
5
|
+
require 'uri'
|
6
|
+
|
7
|
+
attr_reader :access_id, :secret
|
8
|
+
|
9
|
+
# # Sign the request with the specified `access_id` and `secret`.
|
10
|
+
# def sign!(access_id, secret)
|
11
|
+
# @access_id, @secret = access_id, secret
|
12
|
+
|
13
|
+
# #self.sign_with = access_id
|
14
|
+
# #CrashLog::AuthHMAC.keys[access_id] = secret
|
15
|
+
# end
|
16
|
+
|
17
|
+
class HMACAuthentication < Faraday::Middleware
|
18
|
+
# Modified CanonicalString to know how to pull from the Faraday-specific
|
19
|
+
# env hash.
|
20
|
+
class CanonicalString < CrashLog::AuthHMAC::CanonicalString
|
21
|
+
def request_method(request)
|
22
|
+
request[:method].to_s.upcase
|
23
|
+
end
|
24
|
+
|
25
|
+
def request_body(request)
|
26
|
+
request[:body]
|
27
|
+
end
|
28
|
+
|
29
|
+
# def request_path(request)
|
30
|
+
# URI.parse(request[:url]).path
|
31
|
+
# end
|
32
|
+
|
33
|
+
def request_path(request)
|
34
|
+
URI.parse(request[:url].to_s).path
|
35
|
+
end
|
36
|
+
|
37
|
+
def headers(request)
|
38
|
+
request[:request_headers]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
KEY = "Authorization".freeze
|
43
|
+
|
44
|
+
attr_reader :auth, :token, :secret
|
45
|
+
|
46
|
+
def initialize(app, token, secret, options = {})
|
47
|
+
options.merge!(:signature => HMACAuthentication::CanonicalString)
|
48
|
+
keys = {token => secret}
|
49
|
+
@token, @secret = token, secret
|
50
|
+
@auth = CrashLog::AuthHMAC.new(keys, options)
|
51
|
+
super(app)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Public
|
55
|
+
def call(env)
|
56
|
+
env[:request_headers][KEY] ||= hmac_auth_header(env).to_s #if sign_request?
|
57
|
+
puts env[:request_headers]
|
58
|
+
@app.call(env)
|
59
|
+
end
|
60
|
+
|
61
|
+
def hmac_auth_header(env)
|
62
|
+
auth.authorization(env, token, secret)
|
63
|
+
end
|
64
|
+
|
65
|
+
def sign_request?
|
66
|
+
!!@token && !!@secret
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
Faraday.register_middleware :request, :hmac_authentication => Faraday::Request::HMACAuthentication
|
72
|
+
end
|
73
|
+
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
|
3
|
+
class CrashlogGenerator < Rails::Generators::Base
|
4
|
+
|
5
|
+
class_option :api_key, :aliases => "-k", :type => :string, :desc => "Your CrashLog API key"
|
6
|
+
|
7
|
+
def self.source_root
|
8
|
+
@_crashlog_source_root ||= File.expand_path("../../../../../generators/crashlog/templates", __FILE__)
|
9
|
+
end
|
10
|
+
|
11
|
+
def install
|
12
|
+
ensure_api_key_was_configured
|
13
|
+
generate_initializer unless api_key_configured?
|
14
|
+
test_crashlog
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def ensure_api_key_was_configured
|
20
|
+
if !options[:api_key] && !api_key_configured?
|
21
|
+
puts "Must pass --api-key or create config/initializers/crashlog.rb"
|
22
|
+
exit
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def api_key_expression
|
27
|
+
"'#{options[:api_key]}'"
|
28
|
+
end
|
29
|
+
|
30
|
+
def generate_initializer
|
31
|
+
template 'initializer.rb', 'config/initializers/crashlog.rb'
|
32
|
+
end
|
33
|
+
|
34
|
+
def api_key_configured?
|
35
|
+
File.exists?('config/initializers/crashlog.rb')
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_crashlog
|
39
|
+
puts run("rake crashlog:test")
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
data/rails/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "crash_log/rails"
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe CrashLog::Backtrace do
|
4
|
+
let(:raised_error) do
|
5
|
+
begin
|
6
|
+
raise RuntimeError, "This broke"
|
7
|
+
rescue RuntimeError => e
|
8
|
+
e
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
describe '.parse' do
|
13
|
+
it 'accepts a standed caller array' do
|
14
|
+
CrashLog::Backtrace.parse(caller).to_a.size.should == caller.size
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'accepts a ruby exception backtrace' do
|
18
|
+
CrashLog::Backtrace.parse(raised_error.backtrace).to_a.size.should ==
|
19
|
+
raised_error.backtrace.size
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe 'lines' do
|
24
|
+
it 'are converted into a parsed Line object' do
|
25
|
+
CrashLog::Backtrace.parse(raised_error.backtrace).lines.first.should be_a(CrashLog::Backtrace::Line)
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'responds to to_hash' do
|
29
|
+
CrashLog::Backtrace.parse(raised_error.backtrace).lines.first.to_hash.should have_keys(:number, :method, :file)
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'captures line context' do
|
33
|
+
line = CrashLog::Backtrace.parse(raised_error.backtrace).lines.first
|
34
|
+
line.context_line.should match /raise RuntimeError/
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'captures pre_context' do
|
38
|
+
line = CrashLog::Backtrace.parse(raised_error.backtrace).lines.first
|
39
|
+
line.pre_context.should == [
|
40
|
+
"require \\'spec_helper\\'",
|
41
|
+
"",
|
42
|
+
"describe CrashLog::Backtrace do",
|
43
|
+
" let(:raised_error) do",
|
44
|
+
" begin"
|
45
|
+
]
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'captures pre_context' do
|
49
|
+
line = CrashLog::Backtrace.parse(raised_error.backtrace).lines.first
|
50
|
+
line.post_context.should == [
|
51
|
+
" rescue RuntimeError => e",
|
52
|
+
" e",
|
53
|
+
" end",
|
54
|
+
" end",
|
55
|
+
""
|
56
|
+
]
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe 'filters' do
|
61
|
+
it 'should replace project root with [PROJECT_ROOT]' do
|
62
|
+
CrashLog.configuration.project_root = File.expand_path('../../', __FILE__)
|
63
|
+
filters = CrashLog.configuration.backtrace_filters
|
64
|
+
backtrace = CrashLog::Backtrace.parse(raised_error.backtrace, :filters => filters)
|
65
|
+
|
66
|
+
backtrace.lines.first.file.should match /\[PROJECT_ROOT\]/
|
67
|
+
backtrace.lines.first.file.should == '[PROJECT_ROOT]/crash_log/backtrace_spec.rb'
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'should not replace project root if it is not set' do
|
71
|
+
CrashLog.configuration.project_root = nil
|
72
|
+
|
73
|
+
filter = CrashLog::Configuration::DEFAULT_BACKTRACE_FILTERS
|
74
|
+
backtrace = CrashLog::Backtrace.parse(raised_error.backtrace, :filters => filter)
|
75
|
+
|
76
|
+
backtrace.lines.first.file.should_not match /\[PROJECT_ROOT\]/
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|