bugsnag-maglev- 2.8.12
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.
- checksums.yaml +7 -0
- data/.document +5 -0
- data/.gitignore +52 -0
- data/.rspec +3 -0
- data/.travis.yml +15 -0
- data/CHANGELOG.md +425 -0
- data/CONTRIBUTING.md +43 -0
- data/Gemfile +2 -0
- data/LICENSE.txt +20 -0
- data/README.md +804 -0
- data/Rakefile +29 -0
- data/VERSION +1 -0
- data/bugsnag.gemspec +32 -0
- data/lib/bugsnag.rb +129 -0
- data/lib/bugsnag/capistrano.rb +7 -0
- data/lib/bugsnag/capistrano2.rb +32 -0
- data/lib/bugsnag/configuration.rb +129 -0
- data/lib/bugsnag/delay/resque.rb +21 -0
- data/lib/bugsnag/delayed_job.rb +57 -0
- data/lib/bugsnag/delivery.rb +18 -0
- data/lib/bugsnag/delivery/synchronous.rb +51 -0
- data/lib/bugsnag/delivery/thread_queue.rb +53 -0
- data/lib/bugsnag/deploy.rb +35 -0
- data/lib/bugsnag/helpers.rb +127 -0
- data/lib/bugsnag/mailman.rb +28 -0
- data/lib/bugsnag/meta_data.rb +7 -0
- data/lib/bugsnag/middleware/callbacks.rb +19 -0
- data/lib/bugsnag/middleware/mailman.rb +13 -0
- data/lib/bugsnag/middleware/rack_request.rb +72 -0
- data/lib/bugsnag/middleware/rails2_request.rb +52 -0
- data/lib/bugsnag/middleware/rails3_request.rb +42 -0
- data/lib/bugsnag/middleware/rake.rb +23 -0
- data/lib/bugsnag/middleware/sidekiq.rb +13 -0
- data/lib/bugsnag/middleware/warden_user.rb +39 -0
- data/lib/bugsnag/middleware_stack.rb +98 -0
- data/lib/bugsnag/notification.rb +452 -0
- data/lib/bugsnag/rack.rb +53 -0
- data/lib/bugsnag/rails.rb +66 -0
- data/lib/bugsnag/rails/action_controller_rescue.rb +62 -0
- data/lib/bugsnag/rails/active_record_rescue.rb +20 -0
- data/lib/bugsnag/rails/controller_methods.rb +44 -0
- data/lib/bugsnag/railtie.rb +78 -0
- data/lib/bugsnag/rake.rb +25 -0
- data/lib/bugsnag/resque.rb +40 -0
- data/lib/bugsnag/sidekiq.rb +38 -0
- data/lib/bugsnag/tasks.rb +3 -0
- data/lib/bugsnag/tasks/bugsnag.cap +48 -0
- data/lib/bugsnag/tasks/bugsnag.rake +89 -0
- data/lib/bugsnag/version.rb +3 -0
- data/lib/generators/bugsnag/bugsnag_generator.rb +24 -0
- data/rails/init.rb +3 -0
- data/spec/code_spec.rb +86 -0
- data/spec/fixtures/crashes/end_of_file.rb +9 -0
- data/spec/fixtures/crashes/short_file.rb +1 -0
- data/spec/fixtures/crashes/start_of_file.rb +9 -0
- data/spec/fixtures/middleware/internal_info_setter.rb +11 -0
- data/spec/fixtures/middleware/public_info_setter.rb +11 -0
- data/spec/fixtures/tasks/Rakefile +15 -0
- data/spec/helper_spec.rb +144 -0
- data/spec/integration_spec.rb +110 -0
- data/spec/middleware_spec.rb +181 -0
- data/spec/notification_spec.rb +822 -0
- data/spec/rack_spec.rb +56 -0
- data/spec/spec_helper.rb +53 -0
- metadata +198 -0
data/Rakefile
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
require 'bundler/gem_tasks'
|
6
|
+
begin
|
7
|
+
Bundler.setup(:default, :development)
|
8
|
+
rescue Bundler::BundlerError => e
|
9
|
+
$stderr.puts e.message
|
10
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
11
|
+
exit e.status_code
|
12
|
+
end
|
13
|
+
|
14
|
+
require 'rdoc/task'
|
15
|
+
RDoc::Task.new do |rdoc|
|
16
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
17
|
+
|
18
|
+
rdoc.rdoc_dir = 'rdoc'
|
19
|
+
rdoc.title = "bugsnag #{version}"
|
20
|
+
rdoc.rdoc_files.include('README*')
|
21
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
22
|
+
end
|
23
|
+
|
24
|
+
# RSpec tasks
|
25
|
+
require 'rspec/core'
|
26
|
+
require "rspec/core/rake_task"
|
27
|
+
RSpec::Core::RakeTask.new(:spec)
|
28
|
+
|
29
|
+
task :default => :spec
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.8.12
|
data/bugsnag.gemspec
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = "bugsnag-maglev-"
|
3
|
+
s.version = File.read("VERSION").strip
|
4
|
+
|
5
|
+
s.authors = ["James Smith"]
|
6
|
+
s.email = "james@bugsnag.com"
|
7
|
+
|
8
|
+
s.description = "Ruby notifier for bugsnag.com"
|
9
|
+
s.summary = "Ruby notifier for bugsnag.com"
|
10
|
+
s.homepage = "http://github.com/bugsnag/bugsnag-ruby"
|
11
|
+
s.licenses = ["MIT"]
|
12
|
+
|
13
|
+
s.files = `git ls-files`.split("\n").reject {|file| file.start_with? "example/"}
|
14
|
+
s.extra_rdoc_files = [
|
15
|
+
"LICENSE.txt",
|
16
|
+
"README.md"
|
17
|
+
]
|
18
|
+
s.require_paths = ["lib"]
|
19
|
+
|
20
|
+
s.add_dependency 'json', '~> 1.7', '>= 1.7.5'
|
21
|
+
|
22
|
+
if RUBY_VERSION < "1.9"
|
23
|
+
s.add_development_dependency "rake", "~> 10.1.1"
|
24
|
+
else
|
25
|
+
s.add_development_dependency 'rake'
|
26
|
+
end
|
27
|
+
|
28
|
+
s.add_development_dependency 'rspec'
|
29
|
+
s.add_development_dependency 'rdoc'
|
30
|
+
s.add_development_dependency 'pry'
|
31
|
+
s.add_development_dependency 'webmock'
|
32
|
+
end
|
data/lib/bugsnag.rb
ADDED
@@ -0,0 +1,129 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "thread"
|
3
|
+
|
4
|
+
require "bugsnag/version"
|
5
|
+
require "bugsnag/configuration"
|
6
|
+
require "bugsnag/meta_data"
|
7
|
+
require "bugsnag/notification"
|
8
|
+
require "bugsnag/helpers"
|
9
|
+
require "bugsnag/deploy"
|
10
|
+
|
11
|
+
require "bugsnag/delivery"
|
12
|
+
require "bugsnag/delivery/synchronous"
|
13
|
+
require "bugsnag/delivery/thread_queue"
|
14
|
+
|
15
|
+
require "bugsnag/rack"
|
16
|
+
require "bugsnag/railtie" if defined?(Rails::Railtie)
|
17
|
+
|
18
|
+
require "bugsnag/middleware/rack_request"
|
19
|
+
require "bugsnag/middleware/warden_user"
|
20
|
+
require "bugsnag/middleware/callbacks"
|
21
|
+
require "bugsnag/middleware/rails3_request"
|
22
|
+
require "bugsnag/middleware/sidekiq"
|
23
|
+
require "bugsnag/middleware/mailman"
|
24
|
+
require "bugsnag/middleware/rake"
|
25
|
+
require "bugsnag/middleware/callbacks"
|
26
|
+
|
27
|
+
module Bugsnag
|
28
|
+
LOG_PREFIX = "** [Bugsnag] "
|
29
|
+
LOCK = Mutex.new
|
30
|
+
|
31
|
+
class << self
|
32
|
+
# Configure the Bugsnag notifier application-wide settings.
|
33
|
+
def configure(config_hash=nil)
|
34
|
+
if config_hash
|
35
|
+
config_hash.each do |k,v|
|
36
|
+
configuration.send("#{k}=", v) rescue nil if configuration.respond_to?("#{k}=")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
yield(configuration) if block_given?
|
41
|
+
|
42
|
+
# Use resque for asynchronous notification if required
|
43
|
+
require "bugsnag/delay/resque" if configuration.delay_with_resque && defined?(Resque)
|
44
|
+
|
45
|
+
# Log that we are ready to rock
|
46
|
+
if configuration.api_key && !@logged_ready
|
47
|
+
log "Bugsnag exception handler #{VERSION} ready, api_key=#{configuration.api_key}"
|
48
|
+
@logged_ready = true
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Explicitly notify of an exception
|
53
|
+
def notify(exception, overrides=nil, request_data=nil)
|
54
|
+
notification = Notification.new(exception, configuration, overrides, request_data)
|
55
|
+
notification.deliver
|
56
|
+
notification
|
57
|
+
end
|
58
|
+
|
59
|
+
# Notify of an exception unless it should be ignored
|
60
|
+
def notify_or_ignore(exception, overrides=nil, request_data=nil)
|
61
|
+
notification = Notification.new(exception, configuration, overrides, request_data)
|
62
|
+
|
63
|
+
unless notification.ignore?
|
64
|
+
notification.deliver
|
65
|
+
notification
|
66
|
+
else
|
67
|
+
false
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# Auto notify of an exception, called from rails and rack exception
|
72
|
+
# rescuers, unless auto notification is disabled, or we should ignore this
|
73
|
+
# error class
|
74
|
+
def auto_notify(exception, overrides=nil, request_data=nil)
|
75
|
+
overrides ||= {}
|
76
|
+
overrides.merge!({:severity => "error"})
|
77
|
+
notify_or_ignore(exception, overrides, request_data) if configuration.auto_notify
|
78
|
+
end
|
79
|
+
|
80
|
+
# Log wrapper
|
81
|
+
def log(message)
|
82
|
+
configuration.logger.info("#{LOG_PREFIX}#{message}")
|
83
|
+
end
|
84
|
+
|
85
|
+
# Warning logger
|
86
|
+
def warn(message)
|
87
|
+
configuration.logger.warn("#{LOG_PREFIX}#{message}")
|
88
|
+
end
|
89
|
+
|
90
|
+
# Debug logger
|
91
|
+
def debug(message)
|
92
|
+
configuration.logger.info("#{LOG_PREFIX}#{message}") if configuration.debug
|
93
|
+
end
|
94
|
+
|
95
|
+
# Configuration getters
|
96
|
+
def configuration
|
97
|
+
@configuration || LOCK.synchronize { @configuration ||= Bugsnag::Configuration.new }
|
98
|
+
end
|
99
|
+
|
100
|
+
# Set "per-request" data, temporal data for use in bugsnag middleware
|
101
|
+
def set_request_data(key, value)
|
102
|
+
Bugsnag.configuration.set_request_data(key, value)
|
103
|
+
end
|
104
|
+
|
105
|
+
# Clear all "per-request" data, temporal data for use in bugsnag middleware
|
106
|
+
# This method should be called after each distinct request or session ends
|
107
|
+
# Eg. After completing a page request in a web app
|
108
|
+
def clear_request_data
|
109
|
+
Bugsnag.configuration.clear_request_data
|
110
|
+
end
|
111
|
+
|
112
|
+
# Allow access to "before notify" callbacks
|
113
|
+
def before_notify_callbacks
|
114
|
+
Bugsnag.configuration.request_data[:before_callbacks] ||= []
|
115
|
+
end
|
116
|
+
|
117
|
+
# Allow access to "after notify" callbacks
|
118
|
+
def after_notify_callbacks
|
119
|
+
Bugsnag.configuration.request_data[:after_callbacks] ||= []
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
[:resque, :sidekiq, :mailman, :delayed_job].each do |integration|
|
125
|
+
begin
|
126
|
+
require "bugsnag/#{integration}"
|
127
|
+
rescue LoadError
|
128
|
+
end
|
129
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Bugsnag
|
2
|
+
module Capistrano
|
3
|
+
def self.load_into(configuration)
|
4
|
+
configuration.load do
|
5
|
+
after "deploy", "bugsnag:deploy"
|
6
|
+
after "deploy:migrations", "bugsnag:deploy"
|
7
|
+
|
8
|
+
namespace :bugsnag do
|
9
|
+
desc "Notify Bugsnag that new production code has been deployed"
|
10
|
+
task :deploy, :except => { :no_release => true }, :on_error => :continue do
|
11
|
+
begin
|
12
|
+
Bugsnag::Deploy.notify({
|
13
|
+
:api_key => fetch(:bugsnag_api_key, ENV["BUGSNAG_API_KEY"]),
|
14
|
+
:release_stage => fetch(:rails_env, ENV["BUGSNAG_RELEASE_STAGE"] || "production"),
|
15
|
+
:revision => fetch(:current_revision, ENV["BUGSNAG_REVISION"]),
|
16
|
+
:repository => fetch(:repository, ENV["BUGSNAG_REPOSITORY"]),
|
17
|
+
:branch => fetch(:branch, ENV["BUGSNAG_BRANCH"],
|
18
|
+
:app_version => fetch(:app_version, ENV["BUGSNAG_APP_VERSION"]))
|
19
|
+
})
|
20
|
+
rescue
|
21
|
+
logger.important("Bugnsag deploy notification failed, #{$!.inspect}")
|
22
|
+
end
|
23
|
+
|
24
|
+
logger.info "Bugsnag deploy notification complete."
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
Bugsnag::Capistrano.load_into(Capistrano::Configuration.instance) if Capistrano::Configuration.instance
|
@@ -0,0 +1,129 @@
|
|
1
|
+
require "set"
|
2
|
+
require "socket"
|
3
|
+
require "logger"
|
4
|
+
require "bugsnag/middleware_stack"
|
5
|
+
|
6
|
+
module Bugsnag
|
7
|
+
class Configuration
|
8
|
+
attr_accessor :api_key
|
9
|
+
attr_accessor :release_stage
|
10
|
+
attr_accessor :notify_release_stages
|
11
|
+
attr_accessor :auto_notify
|
12
|
+
attr_accessor :use_ssl
|
13
|
+
attr_accessor :ca_file
|
14
|
+
attr_accessor :send_environment
|
15
|
+
attr_accessor :send_code
|
16
|
+
attr_accessor :project_root
|
17
|
+
attr_accessor :app_version
|
18
|
+
attr_accessor :app_type
|
19
|
+
attr_accessor :params_filters
|
20
|
+
attr_accessor :ignore_user_agents
|
21
|
+
attr_accessor :endpoint
|
22
|
+
attr_accessor :logger
|
23
|
+
attr_accessor :middleware
|
24
|
+
attr_accessor :internal_middleware
|
25
|
+
attr_accessor :delay_with_resque
|
26
|
+
attr_accessor :debug
|
27
|
+
attr_accessor :proxy_host
|
28
|
+
attr_accessor :proxy_port
|
29
|
+
attr_accessor :proxy_user
|
30
|
+
attr_accessor :proxy_password
|
31
|
+
attr_accessor :timeout
|
32
|
+
attr_accessor :hostname
|
33
|
+
attr_accessor :delivery_method
|
34
|
+
attr_writer :ignore_classes
|
35
|
+
|
36
|
+
THREAD_LOCAL_NAME = "bugsnag_req_data"
|
37
|
+
|
38
|
+
DEFAULT_ENDPOINT = "notify.bugsnag.com"
|
39
|
+
|
40
|
+
DEFAULT_PARAMS_FILTERS = [
|
41
|
+
/authorization/i,
|
42
|
+
/cookie/i,
|
43
|
+
/password/i,
|
44
|
+
/secret/i,
|
45
|
+
"rack.request.form_vars"
|
46
|
+
].freeze
|
47
|
+
|
48
|
+
DEFAULT_IGNORE_CLASSES = [
|
49
|
+
"AbstractController::ActionNotFound",
|
50
|
+
"ActionController::InvalidAuthenticityToken",
|
51
|
+
"ActionController::ParameterMissing",
|
52
|
+
"ActionController::RoutingError",
|
53
|
+
"ActionController::UnknownAction",
|
54
|
+
"ActionController::UnknownFormat",
|
55
|
+
"ActionController::UnknownHttpMethod",
|
56
|
+
"ActiveRecord::RecordNotFound",
|
57
|
+
"CGI::Session::CookieStore::TamperedWithCookie",
|
58
|
+
"Mongoid::Errors::DocumentNotFound",
|
59
|
+
"SignalException",
|
60
|
+
"SystemExit",
|
61
|
+
].freeze
|
62
|
+
|
63
|
+
DEFAULT_IGNORE_USER_AGENTS = [].freeze
|
64
|
+
|
65
|
+
DEFAULT_DELIVERY_METHOD = :thread_queue
|
66
|
+
|
67
|
+
def initialize
|
68
|
+
@mutex = Mutex.new
|
69
|
+
|
70
|
+
# Set up the defaults
|
71
|
+
self.auto_notify = true
|
72
|
+
self.use_ssl = true
|
73
|
+
self.send_environment = false
|
74
|
+
self.send_code = true
|
75
|
+
self.params_filters = Set.new(DEFAULT_PARAMS_FILTERS)
|
76
|
+
self.ignore_classes = Set.new(DEFAULT_IGNORE_CLASSES)
|
77
|
+
self.ignore_user_agents = Set.new(DEFAULT_IGNORE_USER_AGENTS)
|
78
|
+
self.endpoint = DEFAULT_ENDPOINT
|
79
|
+
self.hostname = default_hostname
|
80
|
+
self.delivery_method = DEFAULT_DELIVERY_METHOD
|
81
|
+
self.timeout = 15
|
82
|
+
|
83
|
+
# Read the API key from the environment
|
84
|
+
self.api_key = ENV["BUGSNAG_API_KEY"]
|
85
|
+
|
86
|
+
# Set up logging
|
87
|
+
self.logger = Logger.new(STDOUT)
|
88
|
+
self.logger.level = Logger::WARN
|
89
|
+
|
90
|
+
# Configure the bugsnag middleware stack
|
91
|
+
self.internal_middleware = Bugsnag::MiddlewareStack.new
|
92
|
+
|
93
|
+
self.middleware = Bugsnag::MiddlewareStack.new
|
94
|
+
self.middleware.use Bugsnag::Middleware::Callbacks
|
95
|
+
end
|
96
|
+
|
97
|
+
# Accept both String and Class instances as an ignored class
|
98
|
+
def ignore_classes
|
99
|
+
@mutex.synchronize { @ignore_classes.map! { |klass| klass.is_a?(Class) ? klass.name : klass } }
|
100
|
+
end
|
101
|
+
|
102
|
+
def should_notify?
|
103
|
+
@release_stage.nil? || @notify_release_stages.nil? || @notify_release_stages.include?(@release_stage)
|
104
|
+
end
|
105
|
+
|
106
|
+
def request_data
|
107
|
+
Thread.current[THREAD_LOCAL_NAME] ||= {}
|
108
|
+
end
|
109
|
+
|
110
|
+
def set_request_data(key, value)
|
111
|
+
self.request_data[key] = value
|
112
|
+
end
|
113
|
+
|
114
|
+
def unset_request_data(key, value)
|
115
|
+
self.request_data.delete(key)
|
116
|
+
end
|
117
|
+
|
118
|
+
def clear_request_data
|
119
|
+
Thread.current[THREAD_LOCAL_NAME] = nil
|
120
|
+
end
|
121
|
+
|
122
|
+
private
|
123
|
+
|
124
|
+
def default_hostname
|
125
|
+
# Don't send the hostname on Heroku
|
126
|
+
Socket.gethostname unless ENV["DYNO"]
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Bugsnag
|
2
|
+
module Delay
|
3
|
+
class Resque
|
4
|
+
@queue = "bugsnag"
|
5
|
+
def self.perform(*args)
|
6
|
+
Bugsnag::Notification.deliver_exception_payload_without_resque(*args)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
Bugsnag::Notification.class_eval do
|
13
|
+
class << self
|
14
|
+
def deliver_exception_payload_with_resque(*args)
|
15
|
+
Resque.enqueue(Bugsnag::Delay::Resque, *args)
|
16
|
+
end
|
17
|
+
|
18
|
+
alias_method :deliver_exception_payload_without_resque, :deliver_exception_payload
|
19
|
+
alias_method :deliver_exception_payload, :deliver_exception_payload_with_resque
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'delayed_job'
|
2
|
+
|
3
|
+
# See Issue #99
|
4
|
+
unless defined?(Delayed::Plugin)
|
5
|
+
raise LoadError, "bugsnag requires delayed_job > 3.x"
|
6
|
+
end
|
7
|
+
|
8
|
+
unless defined? Delayed::Plugins::Bugsnag
|
9
|
+
module Delayed
|
10
|
+
module Plugins
|
11
|
+
|
12
|
+
|
13
|
+
class Bugsnag < Plugin
|
14
|
+
module Notify
|
15
|
+
def error(job, error)
|
16
|
+
overrides = {
|
17
|
+
:job => {
|
18
|
+
:class => job.class.name,
|
19
|
+
:id => job.id,
|
20
|
+
}
|
21
|
+
}
|
22
|
+
if payload = job.payload_object
|
23
|
+
p = {
|
24
|
+
:class => payload.class.name,
|
25
|
+
}
|
26
|
+
p[:id] = payload.id if payload.respond_to?(:id)
|
27
|
+
p[:display_name] = payload.display_name if payload.respond_to?(:display_name)
|
28
|
+
p[:method_name] = payload.method_name if payload.respond_to?(:method_name)
|
29
|
+
p[:args] = payload.args if payload.respond_to?(:args)
|
30
|
+
if payload.is_a?(::Delayed::PerformableMethod) && (object = payload.object)
|
31
|
+
p[:object] = {
|
32
|
+
:class => object.class.name,
|
33
|
+
}
|
34
|
+
p[:object][:id] = object.id if object.respond_to?(:id)
|
35
|
+
end
|
36
|
+
overrides[:job][:payload] = p
|
37
|
+
end
|
38
|
+
|
39
|
+
::Bugsnag.auto_notify(error, overrides)
|
40
|
+
|
41
|
+
super if defined?(super)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
callbacks do |lifecycle|
|
46
|
+
lifecycle.before(:invoke_job) do |job|
|
47
|
+
payload = job.payload_object
|
48
|
+
payload = payload.object if payload.is_a? Delayed::PerformableMethod
|
49
|
+
payload.extend Notify
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
Delayed::Worker.plugins << Delayed::Plugins::Bugsnag
|
57
|
+
end
|