bugsnag 1.1.5 → 1.2.0.beta

Sign up to get free protection for your applications and to get access to all the features.
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