bugsnag 1.1.5 → 1.2.0.beta

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/Gemfile CHANGED
@@ -3,7 +3,7 @@ source "http://rubygems.org"
3
3
  # Example:
4
4
  # gem "activesupport", ">= 2.3.5"
5
5
  gem "multi_json", "~> 1.0"
6
- gem "httparty", "~> 0.8.3"
6
+ gem "httparty", ">= 0.5", "< 1.0"
7
7
 
8
8
  # Add dependencies to develop your gem here.
9
9
  # Include everything needed to run rake, tests, features, etc.
@@ -1,14 +1,14 @@
1
1
  GEM
2
2
  remote: http://rubygems.org/
3
3
  specs:
4
- activesupport (3.2.6)
4
+ activesupport (3.2.8)
5
5
  i18n (~> 0.6)
6
6
  multi_json (~> 1.0)
7
7
  git (1.2.5)
8
- httparty (0.8.3)
8
+ httparty (0.9.0)
9
9
  multi_json (~> 1.0)
10
10
  multi_xml
11
- i18n (0.6.0)
11
+ i18n (0.6.1)
12
12
  jeweler (1.6.4)
13
13
  bundler (~> 1.0)
14
14
  git (>= 1.2.5)
@@ -21,14 +21,14 @@ GEM
21
21
  shoulda-context (~> 1.0)
22
22
  shoulda-matchers (~> 1.2)
23
23
  shoulda-context (1.0.0)
24
- shoulda-matchers (1.2.0)
24
+ shoulda-matchers (1.3.0)
25
25
  activesupport (>= 3.0.0)
26
26
 
27
27
  PLATFORMS
28
28
  ruby
29
29
 
30
30
  DEPENDENCIES
31
- httparty (~> 0.8.3)
31
+ httparty (>= 0.5, < 1.0)
32
32
  jeweler (~> 1.6.4)
33
33
  multi_json (~> 1.0)
34
34
  rcov
data/README.md CHANGED
@@ -12,8 +12,19 @@ as fast as possible. [Create a free account](http://bugsnag.com) to start
12
12
  capturing exceptions from your applications.
13
13
 
14
14
 
15
- How to Install (Rails)
16
- ----------------------
15
+ Contents
16
+ --------
17
+
18
+ - [How to Install](#how-to-install)
19
+ - [Sending Custom Data With Exceptions](#sending-custom-data-with-exceptions)
20
+ - [Sending Non-Fatal Exceptions](#sending-non-fatal-exceptions)
21
+ - [Configuration](#configuration)
22
+ - [Bugsnag Middleware](#bugsnag-middleware)
23
+ - [Deploy Tracking](#deploy-tracking)
24
+
25
+
26
+ How to Install
27
+ --------------
17
28
 
18
29
  1. Add the `bugsnag` gem to your `Gemfile`
19
30
 
@@ -27,7 +38,9 @@ How to Install (Rails)
27
38
  bundle install
28
39
  ```
29
40
 
30
- 3. Copy the following code to a new file at `config/initializers/bugsnag.rb`
41
+ 3. Configure the Bugsnag module with your API key.
42
+
43
+ In rails apps, put this code to a new file at `config/initializers/bugsnag.rb`
31
44
 
32
45
  ```ruby
33
46
  Bugsnag.configure do |config|
@@ -35,45 +48,87 @@ How to Install (Rails)
35
48
  end
36
49
  ```
37
50
 
38
- How to Install (Sinatra)
39
- ------------------------
51
+ 4. **Rack/Sinatra apps only**: Activate the Bugsnag Rack middleware
40
52
 
41
- ```ruby
42
- require "bugsnag"
53
+ ```ruby
54
+ use Bugsnag::Rack
55
+ ```
43
56
 
44
- Bugsnag.configure do |config|
45
- config.api_key = "YOUR_API_KEY_HERE"
57
+
58
+ Sending Custom Data With Exceptions
59
+ -----------------------------------
60
+
61
+ It is often useful to send additional meta-data about your app, such as
62
+ information about the currently logged in user, along with any
63
+ exceptions, to help debug problems.
64
+
65
+ ### Rails Apps
66
+
67
+ In any rails controller you can define a `before_bugsnag_notify` callback,
68
+ which allows you to add this additional data by calling `add_tab` on the
69
+ exception notification object.
70
+
71
+ ```ruby
72
+ class MyController < ApplicationController
73
+ # Define the filter
74
+ before_bugsnag_notify :add_user_info_to_bugsnag
75
+
76
+ # Your controller code here
77
+
78
+ private
79
+ def add_user_info_to_bugsnag(notif)
80
+ # Add some app-specific data which will be displayed on a custom
81
+ # "User Info" tab on each error page on bugsnag.com
82
+ notif.add_tab(:user_info, {
83
+ name: current_user.name
84
+ })
85
+ end
46
86
  end
87
+ ```
88
+
89
+ ### Other Ruby Apps
90
+
91
+ In other ruby apps, you can provide lambda functions to execute before any
92
+ `Bugsnag.notify` calls as follows. Don't forget to clear the callbacks at the
93
+ end of each request or session.
94
+
95
+ ```ruby
96
+ # Set a before notify callback
97
+ Bugsnag.before_notify_callbacks << lambda {|notif|
98
+ notif.add_tab(:user_info, {
99
+ name: current_user.name
100
+ })
101
+ }
47
102
 
48
- use Bugsnag::Rack
103
+ # Your app code here
104
+
105
+ # Clear the callbacks
106
+ Bugsnag.before_notify_callbacks.clear
49
107
  ```
50
108
 
109
+ You can read more about how callbacks work in the
110
+ [Bugsnag Middleware](#bugsnag-middleware) documentation below.
111
+
51
112
 
52
- Send Non-Fatal Exceptions to Bugsnag
53
- ------------------------------------
113
+ Sending Non-Fatal Exceptions
114
+ ----------------------------
54
115
 
55
- If you would like to send non-fatal exceptions to Bugsnag, there are two
56
- ways of doing so. From a rails controller, you can call `notify_bugsnag`:
116
+ If you would like to send non-fatal exceptions to Bugsnag, you can call
117
+ `Bugsnag.notify`:
57
118
 
58
119
  ```ruby
59
- notify_bugsnag(RuntimeError.new("Something broke"))
120
+ Bugsnag.notify(RuntimeError.new("Something broke"))
60
121
  ```
61
122
 
62
123
  You can also send additional meta-data with your exception:
63
124
 
64
125
  ```ruby
65
- notify_bugsnag(RuntimeError.new("Something broke"), {
126
+ Bugsnag.notify(RuntimeError.new("Something broke"), {
66
127
  :username => "bob-hoskins",
67
128
  :registered_user => true
68
129
  })
69
130
  ```
70
131
 
71
- Anywhere else in your ruby code, you can call `Bugsnag.notify`:
72
-
73
- ```ruby
74
- Bugsnag.notify(RuntimeError.new("Something broke"));
75
- ```
76
-
77
132
 
78
133
  Configuration
79
134
  -------------
@@ -195,6 +250,61 @@ By default, `ignore_classes` contains the following classes:
195
250
  ]
196
251
  ```
197
252
 
253
+ ###logger
254
+
255
+ Sets which logger to use for Bugsnag log messages. In rails apps, this is
256
+ automatically set to use `Rails.logger`, otherwise it will be set to
257
+ `Logger.new(STDOUT)`.
258
+
259
+ ###middleware
260
+
261
+ Provides access to the middleware stack, see the
262
+ [Bugsnag Middleware](#bugsnag-middleware) section below for details.
263
+
264
+
265
+ Bugsnag Middleware
266
+ ------------------
267
+
268
+ The Bugsnag Notifier for Ruby provides its own middleware system, similar to
269
+ the one used in Rack applications. Middleware allows you to execute code
270
+ before and after an exception is sent to bugsnag.com, so you can do things
271
+ such as:
272
+
273
+ - Send application-specific information along with exceptions, eg. the name
274
+ of the currently logged in user,
275
+ - Write exception information to your internal logging system.
276
+
277
+ To make your own middleware, create a class that looks like this:
278
+
279
+ ```ruby
280
+ class MyMiddleware
281
+ def initialize(bugsnag)
282
+ @bugsnag = bugsnag
283
+ end
284
+
285
+ def call(notification)
286
+ # Your custom "before notify" code
287
+
288
+ @bugsnag.call(notification)
289
+
290
+ # Your custom "after notify" code
291
+ end
292
+ end
293
+ ```
294
+
295
+ You can then add your middleware to the middleware stack as follows:
296
+
297
+ ```ruby
298
+ Bugsnag.configure do |config|
299
+ config.middleware.use MyMiddleware
300
+ end
301
+ ```
302
+
303
+ You can also view the order of the currently activated middleware by running `rake bugsnag:middleware`.
304
+
305
+ Check out Bugsnag's [built in middleware classes](https://github.com/bugsnag/bugsnag-ruby/tree/master/lib/bugsnag/middleware)
306
+ for some real examples of middleware in action.
307
+
198
308
 
199
309
  Deploy Tracking
200
310
  ---------------
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.1.5
1
+ 1.2.0.beta
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "bugsnag"
8
- s.version = "1.1.5"
8
+ s.version = "1.2.0.beta"
9
9
 
10
- s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
10
+ s.required_rubygems_version = Gem::Requirement.new("> 1.3.1") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["James Smith"]
12
- s.date = "2012-09-21"
12
+ s.date = "2012-09-29"
13
13
  s.description = "Ruby notifier for bugsnag.com"
14
14
  s.email = "james@bugsnag.com"
15
15
  s.extra_rdoc_files = [
@@ -28,8 +28,13 @@ Gem::Specification.new do |s|
28
28
  "lib/bugsnag.rb",
29
29
  "lib/bugsnag/capistrano.rb",
30
30
  "lib/bugsnag/configuration.rb",
31
- "lib/bugsnag/delay/resque.rb",
32
31
  "lib/bugsnag/helpers.rb",
32
+ "lib/bugsnag/middleware/callbacks.rb",
33
+ "lib/bugsnag/middleware/rack_request.rb",
34
+ "lib/bugsnag/middleware/rails2_request.rb",
35
+ "lib/bugsnag/middleware/rails3_request.rb",
36
+ "lib/bugsnag/middleware/warden_user.rb",
37
+ "lib/bugsnag/middleware_stack.rb",
33
38
  "lib/bugsnag/notification.rb",
34
39
  "lib/bugsnag/rack.rb",
35
40
  "lib/bugsnag/rails.rb",
@@ -37,9 +42,9 @@ Gem::Specification.new do |s|
37
42
  "lib/bugsnag/rails/controller_methods.rb",
38
43
  "lib/bugsnag/railtie.rb",
39
44
  "lib/bugsnag/tasks.rb",
45
+ "lib/bugsnag/tasks/bugsnag.rake",
40
46
  "lib/bugsnag/version.rb",
41
47
  "lib/resque/failure/bugsnag.rb",
42
- "lib/tasks/bugsnag.rake",
43
48
  "rails/init.rb",
44
49
  "test/helper.rb",
45
50
  "test/test_bugsnag.rb"
@@ -55,20 +60,20 @@ Gem::Specification.new do |s|
55
60
 
56
61
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
57
62
  s.add_runtime_dependency(%q<multi_json>, ["~> 1.0"])
58
- s.add_runtime_dependency(%q<httparty>, ["~> 0.8.3"])
63
+ s.add_runtime_dependency(%q<httparty>, ["< 1.0", ">= 0.5"])
59
64
  s.add_development_dependency(%q<shoulda>, [">= 0"])
60
65
  s.add_development_dependency(%q<jeweler>, ["~> 1.6.4"])
61
66
  s.add_development_dependency(%q<rcov>, [">= 0"])
62
67
  else
63
68
  s.add_dependency(%q<multi_json>, ["~> 1.0"])
64
- s.add_dependency(%q<httparty>, ["~> 0.8.3"])
69
+ s.add_dependency(%q<httparty>, ["< 1.0", ">= 0.5"])
65
70
  s.add_dependency(%q<shoulda>, [">= 0"])
66
71
  s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
67
72
  s.add_dependency(%q<rcov>, [">= 0"])
68
73
  end
69
74
  else
70
75
  s.add_dependency(%q<multi_json>, ["~> 1.0"])
71
- s.add_dependency(%q<httparty>, ["~> 0.8.3"])
76
+ s.add_dependency(%q<httparty>, ["< 1.0", ">= 0.5"])
72
77
  s.add_dependency(%q<shoulda>, [">= 0"])
73
78
  s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
74
79
  s.add_dependency(%q<rcov>, [">= 0"])
@@ -14,12 +14,15 @@ module Bugsnag
14
14
  LOG_PREFIX = "** [Bugsnag] "
15
15
 
16
16
  class << self
17
- # Configure the gem to send notifications, at the very least an api_key is required
18
- def configure
19
- yield(configuration)
17
+ # Configure the Bugsnag notifier application-wide settings.
18
+ def configure(config_hash=nil)
19
+ if config_hash
20
+ config_hash.each do |k,v|
21
+ configuration.send("#{k}=", v) rescue nil if configuration.respond_to?("#{k}=")
22
+ end
23
+ end
20
24
 
21
- # Use resque for asynchronous notification if required
22
- require "bugsnag/delay/resque" if configuration.delay_with_resque && defined?(Resque)
25
+ yield(configuration) if block_given?
23
26
 
24
27
  # Log that we are ready to rock
25
28
  if configuration.api_key && !@logged_ready
@@ -29,40 +32,58 @@ module Bugsnag
29
32
  end
30
33
 
31
34
  # Explicitly notify of an exception
32
- def notify(exception, session_data={})
33
- Notification.new(exception, configuration.merge(session_data)).deliver
35
+ def notify(exception, overrides=nil, request_data=nil)
36
+ Notification.new(exception, configuration, overrides, request_data).deliver
34
37
  end
35
38
 
36
39
  # Notify of an exception unless it should be ignored
37
- def notify_or_ignore(exception, session_data={})
38
- notification = Notification.new(exception, configuration.merge(session_data))
40
+ def notify_or_ignore(exception, overrides=nil, request_data=nil)
41
+ notification = Notification.new(exception, configuration, overrides, request_data)
39
42
  notification.deliver unless notification.ignore?
40
43
  end
41
44
 
42
45
  # Auto notify of an exception, called from rails and rack exception
43
46
  # rescuers, unless auto notification is disabled, or we should ignore this
44
47
  # error class
45
- def auto_notify(exception, session_data={})
46
- notify_or_ignore(exception, session_data) if configuration.auto_notify
48
+ def auto_notify(exception, overrides=nil, request_data=nil)
49
+ notify_or_ignore(exception, overrides, request_data) if configuration.auto_notify
47
50
  end
48
51
 
49
52
  # Log wrapper
50
53
  def log(message)
51
- configuration.logger.info(LOG_PREFIX + message) if configuration.logger
54
+ configuration.logger.info("#{LOG_PREFIX}#{message}")
52
55
  end
53
56
 
54
57
  # Warning logger
55
58
  def warn(message)
56
- if configuration.logger
57
- configuration.logger.warn(LOG_PREFIX + message)
58
- else
59
- puts "#{LOG_PREFIX}#{message}"
60
- end
59
+ configuration.logger.warn("#{LOG_PREFIX}#{message}")
61
60
  end
62
61
 
63
- # Configuration getter
62
+ # Configuration getters
64
63
  def configuration
65
64
  @configuration ||= Bugsnag::Configuration.new
66
65
  end
66
+
67
+ # Set "per-request" data, temporal data for use in bugsnag middleware
68
+ def set_request_data(key, value)
69
+ Bugsnag.configuration.set_request_data(key, value)
70
+ end
71
+
72
+ # Clear all "per-request" data, temporal data for use in bugsnag middleware
73
+ # This method should be called after each distinct request or session ends
74
+ # Eg. After completing a page request in a web app
75
+ def clear_request_data
76
+ Bugsnag.configuration.clear_request_data
77
+ end
78
+
79
+ # Allow access to "before notify" callbacks
80
+ def before_notify_callbacks
81
+ Bugsnag.configuration.request_data[:before_callbacks] ||= []
82
+ end
83
+
84
+ # Allow access to "after notify" callbacks
85
+ def after_notify_callbacks
86
+ Bugsnag.configuration.request_data[:after_callbacks] ||= []
87
+ end
67
88
  end
68
89
  end
@@ -1,36 +1,27 @@
1
+ require "set"
2
+ require "logger"
3
+ require "bugsnag/middleware_stack"
4
+
1
5
  module Bugsnag
2
6
  class Configuration
3
- OPTIONS = [
4
- :api_key, :release_stage, :notify_release_stages, :auto_notify,
5
- :use_ssl, :project_root, :app_version,
6
- :params_filters, :ignore_classes,
7
-
8
- :stacktrace_filters,
9
- :framework, :endpoint, :logger,
10
- :delay_with_resque
11
- ]
12
- OPTIONS.each {|o| attr_accessor o }
7
+ attr_accessor :api_key
8
+ attr_accessor :release_stage
9
+ attr_accessor :notify_release_stages
10
+ attr_accessor :auto_notify
11
+ attr_accessor :use_ssl
12
+ attr_accessor :project_root
13
+ attr_accessor :app_version
14
+ attr_accessor :params_filters
15
+ attr_accessor :ignore_classes
16
+ attr_accessor :endpoint
17
+ attr_accessor :logger
18
+ attr_accessor :middleware
13
19
 
14
- DEFAULT_PARAMS_FILTERS = %w(password password_confirmation).freeze
20
+ THREAD_LOCAL_NAME = "bugsnag_req_data"
15
21
 
16
- DEFAULT_STACKTRACE_FILTERS = [
17
- lambda { |line|
18
- if defined?(Bugsnag.configuration.project_root) && Bugsnag.configuration.project_root.to_s != ''
19
- line.sub(/#{Bugsnag.configuration.project_root}\//, "")
20
- else
21
- line
22
- end
23
- },
24
- lambda { |line| line.gsub(/^\.\//, "") },
25
- lambda { |line|
26
- if defined?(Gem)
27
- Gem.path.inject(line) do |line, path|
28
- line.gsub(/#{path}\//, "")
29
- end
30
- end
31
- },
32
- lambda { |line| line if line !~ %r{lib/bugsnag} }
33
- ].freeze
22
+ DEFAULT_ENDPOINT = "notify.bugsnag.com"
23
+
24
+ DEFAULT_PARAMS_FILTERS = ["password"].freeze
34
25
 
35
26
  DEFAULT_IGNORE_CLASSES = [
36
27
  "ActiveRecord::RecordNotFound",
@@ -40,26 +31,46 @@ module Bugsnag
40
31
  "ActionController::UnknownAction",
41
32
  "AbstractController::ActionNotFound",
42
33
  "Mongoid::Errors::DocumentNotFound"
43
- ]
44
-
34
+ ].freeze
45
35
 
46
36
  def initialize
47
- @params_filters = DEFAULT_PARAMS_FILTERS.dup
48
- @stacktrace_filters = DEFAULT_STACKTRACE_FILTERS.dup
49
- @ignore_classes = DEFAULT_IGNORE_CLASSES.dup
50
- @auto_notify = true
51
- @release_stage = "production"
52
- @notify_release_stages = ["production"]
37
+ # Set up the defaults
38
+ self.release_stage = "production"
39
+ self.notify_release_stages = ["production"]
40
+ self.auto_notify = true
41
+ self.use_ssl = false
42
+ self.params_filters = Set.new(DEFAULT_PARAMS_FILTERS)
43
+ self.ignore_classes = Set.new(DEFAULT_IGNORE_CLASSES)
44
+ self.endpoint = DEFAULT_ENDPOINT
45
+
46
+ # Set up logging
47
+ self.logger = Logger.new(STDOUT)
48
+ self.logger.level = Logger::WARN if self.release_stage == "production"
49
+
50
+ # Configure the bugsnag middleware stack
51
+ self.middleware = Bugsnag::MiddlewareStack.new
52
+ self.middleware.use Bugsnag::Middleware::Callbacks
53
+ end
54
+
55
+ def should_notify?
56
+ @notify_release_stages.include?(@release_stage)
57
+ end
58
+
59
+ def request_data
60
+ Thread.current[THREAD_LOCAL_NAME] ||= {}
61
+ end
62
+
63
+ def set_request_data(key, value)
64
+ Bugsnag.warn "Overwriting request data for key #{key.to_s}" if self.request_data[key]
65
+ self.request_data[key] = value
53
66
  end
54
67
 
55
- def to_hash
56
- OPTIONS.inject({}) do |hash, option|
57
- hash.merge(option.to_sym => send(option))
58
- end
68
+ def unset_request_data(key, value)
69
+ self.request_data.delete(key)
59
70
  end
60
71
 
61
- def merge(hash)
62
- to_hash.merge(hash)
72
+ def clear_request_data
73
+ Thread.current[THREAD_LOCAL_NAME] = nil
63
74
  end
64
75
  end
65
76
  end