sentry-raven 0.2 → 0.3

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of sentry-raven might be problematic. Click here for more details.

data/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # Raven-Ruby
2
2
 
3
- [![Build Status](https://secure.travis-ci.org/coderanger/raven-ruby.png?branch=master)](http://travis-ci.org/coderanger/raven-ruby)
3
+ [![Build Status](https://secure.travis-ci.org/getsentry/raven-ruby.png?branch=master)](http://travis-ci.org/getsentry/raven-ruby)
4
4
 
5
- A client and integration layer for the [Sentry](https://github.com/dcramer/sentry) error reporting API.
5
+ A client and integration layer for the [Sentry](https://github.com/getsentry/sentry) error reporting API.
6
6
 
7
7
  This library is still forming, so if you are looking to just use it, please check back in a few weeks.
8
8
 
@@ -11,7 +11,7 @@ This library is still forming, so if you are looking to just use it, please chec
11
11
  Add the following to your `Gemfile`:
12
12
 
13
13
  ```ruby
14
- gem "sentry-raven", :git => "https://github.com/coderanger/raven-ruby.git"
14
+ gem "sentry-raven", :git => "https://github.com/getsentry/raven-ruby.git"
15
15
  ```
16
16
 
17
17
  Or install manually
@@ -37,7 +37,7 @@ end
37
37
 
38
38
  No support for Rails 2 yet.
39
39
 
40
- ### Other Rack Servers
40
+ ### Rack
41
41
 
42
42
  Basic RackUp file.
43
43
 
@@ -51,6 +51,23 @@ end
51
51
  use Raven::Rack
52
52
  ```
53
53
 
54
+ ### Sinatra
55
+
56
+ ```ruby
57
+ require 'sinatra'
58
+ require 'raven'
59
+
60
+ Raven.configure do |config|
61
+ config.dsn = 'http://public:secret@example.com/project-id'
62
+ end
63
+
64
+ use Raven::Rack
65
+
66
+ get '/' do
67
+ 1 / 0
68
+ end
69
+ ```
70
+
54
71
  ### Other Ruby
55
72
 
56
73
  ```ruby
@@ -90,4 +107,44 @@ Raven.configure do |config|
90
107
  config.dsn = 'http://public:secret@example.com/project-id'
91
108
  config.environments = %w[ development production ]
92
109
  end
93
- ```
110
+ ```
111
+
112
+ ## Excluding Exceptions
113
+
114
+ If you never wish to be notified of certain exceptions, specify 'excluded_exceptions' in your config file.
115
+
116
+ In the example below, the exceptions Rails uses to generate 404 responses will be suppressed.
117
+
118
+ ```ruby
119
+ require 'raven'
120
+
121
+ Raven.configure do |config|
122
+ config.dsn = 'http://public:secret@example.com/project-id'
123
+ config.excluded_exceptions = ['ActionController::RoutingError', 'ActiveRecord::RecordNotFound']
124
+ end
125
+ ```
126
+
127
+ ## Sanitizing Data (Processors)
128
+
129
+ If you need to sanitize or pre-process (before its sent to the server) data, you can do so using the Processors
130
+ implementation. By default, a single processor is installed (Raven::Processors::SanitizeData), which will attempt to
131
+ sanitize keys that match various patterns (e.g. password) and values that resemble credit card numbers.
132
+
133
+ To specify your own (or to remove the defaults), simply pass them with your configuration:
134
+
135
+ ```ruby
136
+ require 'raven'
137
+
138
+ Raven.configure do |config|
139
+ config.dsn = 'http://public:secret@example.com/project-id'
140
+ config.processors = [Raven::Processors::SanitizeData]
141
+ end
142
+
143
+ Resources
144
+ ---------
145
+
146
+ * `Bug Tracker <http://github.com/getsentry/raven-ruby/issues>`_
147
+ * `Code <http://github.com/getsentry/raven-ruby>`_
148
+ * `Mailing List <https://groups.google.com/group/getsentry>`_
149
+ * `IRC <irc://irc.freenode.net/sentry>`_ (irc.freenode.net, #sentry)
150
+
data/lib/raven.rb CHANGED
@@ -8,6 +8,7 @@ require 'raven/interfaces/message'
8
8
  require 'raven/interfaces/exception'
9
9
  require 'raven/interfaces/stack_trace'
10
10
  require 'raven/interfaces/http'
11
+ require 'raven/processors/sanitizedata'
11
12
 
12
13
  require 'raven/railtie' if defined?(Rails::Railtie)
13
14
 
@@ -72,8 +73,7 @@ module Raven
72
73
  rescue Error => e
73
74
  raise # Don't capture Raven errors
74
75
  rescue Exception => e
75
- evt = Event.capture_exception(e)
76
- send(evt) if evt
76
+ self.captureException(e)
77
77
  raise
78
78
  end
79
79
  else
@@ -81,12 +81,21 @@ module Raven
81
81
  at_exit do
82
82
  if $!
83
83
  logger.debug "Caught a post-mortem exception: #{$!.inspect}"
84
- evt = Event.capture_exception($!)
85
- send(evt) if evt
84
+ self.captureException($!)
86
85
  end
87
86
  end
88
87
  end
89
88
  end
90
89
 
90
+ def captureException(exception)
91
+ evt = Event.capture_exception(exception)
92
+ send(evt) if evt
93
+ end
94
+
95
+ def captureMessage(message)
96
+ evt = Event.capture_message(message)
97
+ send(evt) if evt
98
+ end
99
+
91
100
  end
92
101
  end
data/lib/raven/client.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  require 'openssl'
2
2
  require 'uri'
3
- require 'yajl'
3
+ require 'multi_json'
4
4
  require 'faraday'
5
5
 
6
6
  require 'raven/version'
@@ -59,7 +59,7 @@ module Raven
59
59
  Raven.logger.debug "Sending event #{event.id} to Sentry"
60
60
  response = self.conn.post '/api/store/' do |req|
61
61
  req.headers['Content-Type'] = 'application/json'
62
- req.body = Yajl::Encoder.encode(event.to_hash)
62
+ req.body = MultiJson.encode(event.to_hash)
63
63
  req.headers[AUTH_HEADER_KEY] = self.generate_auth_header(req.body)
64
64
  end
65
65
  raise Error.new("Error from Sentry server (#{response.status}): #{response.body}") unless response.status == 200
@@ -25,6 +25,12 @@ module Raven
25
25
  # Include module versions in reports?
26
26
  attr_accessor :send_modules
27
27
 
28
+ # Which exceptions should never be sent
29
+ attr_accessor :excluded_exceptions
30
+
31
+ # Processors to run on data before sending upstream
32
+ attr_accessor :processors
33
+
28
34
  attr_reader :current_environment
29
35
 
30
36
  def initialize
@@ -33,6 +39,8 @@ module Raven
33
39
  self.environments = %w[ production ]
34
40
  self.current_environment = ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'development'
35
41
  self.send_modules = true
42
+ self.excluded_exceptions = []
43
+ self.processors = [Raven::Processor::SanitizeData]
36
44
  end
37
45
 
38
46
  def server=(value)
data/lib/raven/error.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  module Raven
2
2
 
3
- class Error < Exception
3
+ class Error < StandardError
4
4
  end
5
5
 
6
6
  end
data/lib/raven/event.rb CHANGED
@@ -24,7 +24,8 @@ module Raven
24
24
  attr_accessor :logger, :culprit, :server_name, :modules, :extra
25
25
 
26
26
  def initialize(options={}, configuration=nil, &block)
27
- configuration ||= Raven.configuration
27
+ @configuration = configuration || Raven.configuration
28
+ @interfaces = {}
28
29
 
29
30
  @id = options[:id] || UUIDTools::UUID.random_create.hexdigest
30
31
  @message = options[:message]
@@ -32,21 +33,19 @@ module Raven
32
33
  @level = options[:level] || :error
33
34
  @logger = options[:logger] || 'root'
34
35
  @culprit = options[:culprit]
36
+ @extra = options[:extra]
35
37
 
36
38
  # Try to resolve the hostname to an FQDN, but fall back to whatever the load name is
37
39
  hostname = Socket.gethostname
38
40
  hostname = Socket.gethostbyname(hostname).first rescue hostname
39
41
  @server_name = options[:server_name] || hostname
40
42
 
41
- if configuration.send_modules && !options.has_key?(:modules)
42
- options[:modules] = Hash[Gem::Specification.map {|spec| [spec.name, spec.version]}]
43
+ # Older versions of Rubygems don't support iterating over all specs
44
+ if @configuration.send_modules && Gem::Specification.respond_to?(:map)
45
+ options[:modules] ||= Hash[Gem::Specification.map {|spec| [spec.name, spec.version.to_s]}]
43
46
  end
44
47
  @modules = options[:modules]
45
48
 
46
- @extra = options[:extra]
47
-
48
- @interfaces = {}
49
-
50
49
  block.call(self) if block
51
50
 
52
51
  # Some type coercion
@@ -99,36 +98,20 @@ module Raven
99
98
  Raven.logger.info "Refusing to capture Raven error: #{exc.inspect}"
100
99
  return nil
101
100
  end
101
+ if configuration[:excluded_exceptions].include? exc.class.name
102
+ Raven.logger.info "User excluded error: #{exc.inspect}"
103
+ return nil
104
+ end
102
105
  self.new({}, configuration) do |evt|
103
- evt.message = exc.message
106
+ evt.message = "#{exc.class.to_s}: #{exc.message}"
104
107
  evt.level = :error
105
- evt.interface :exception do |int|
106
- int.type = exc.class.to_s
107
- int.value = exc.message
108
- class_parts = exc.class.to_s.split('::')
109
- class_parts.pop
110
- int.module = class_parts.join('::')
111
- end
112
- evt.interface :stack_trace do |int|
113
- int.frames = exc.backtrace.reverse.map do |trace_line|
114
- md = BACKTRACE_RE.match(trace_line)
115
- raise Error.new("Unable to parse backtrace line: #{trace_line.inspect}") unless md
116
- int.frame do |frame|
117
- frame.abs_path = md[1]
118
- frame.lineno = md[2].to_i
119
- frame.function = md[3] if md[3]
120
- lib_path = $:.select{|s| frame.abs_path.start_with?(s)}.sort_by{|s| s.length}.last
121
- if lib_path
122
- frame.filename = frame.abs_path[lib_path.chomp(File::SEPARATOR).length+1..frame.abs_path.length]
123
- else
124
- frame.filename = frame.abs_path
125
- end
126
- if configuration[:context_lines]
127
- frame.context_line = Raven::LineCache::getline(frame.abs_path, frame.lineno)
128
- frame.pre_context = (frame.lineno-configuration[:context_lines]..frame.lineno-1).map{|i| Raven::LineCache.getline(frame.abs_path, i)}.select{|line| line}
129
- frame.post_context = (frame.lineno+1..frame.lineno+configuration[:context_lines]).map{|i| Raven::LineCache.getline(frame.abs_path, i)}.select{|line| line}
130
- end
108
+ evt.parse_exception(exc)
109
+ if (exc.backtrace)
110
+ evt.interface :stack_trace do |int|
111
+ int.frames = exc.backtrace.reverse.map do |trace_line|
112
+ int.frame {|frame| evt.parse_backtrace_line(trace_line, frame) }
131
113
  end
114
+ evt.culprit = evt.get_culprit(int.frames)
132
115
  end
133
116
  end
134
117
  block.call(evt) if block
@@ -145,9 +128,48 @@ module Raven
145
128
  end
146
129
  end
147
130
 
131
+ def self.capture_message(message, configuration=nil)
132
+ configuration ||= Raven.configuration
133
+ self.new({}, configuration) do |evt|
134
+ evt.message = message
135
+ evt.level = :error
136
+ evt.interface :message do |int|
137
+ int.message = message
138
+ end
139
+ end
140
+ end
141
+
142
+ def get_culprit(frames)
143
+ lastframe = frames[-2]
144
+ "#{lastframe.filename} in #{lastframe.function}" if lastframe
145
+ end
146
+
147
+ def parse_exception(exception)
148
+ interface(:exception) do |int|
149
+ int.type = exception.class.to_s
150
+ int.value = exception.message
151
+ int.module = exception.class.to_s.split('::')[0...-1].join('::')
152
+ end
153
+ end
154
+
155
+ def parse_backtrace_line(line, frame)
156
+ md = BACKTRACE_RE.match(line)
157
+ raise Error.new("Unable to parse backtrace line: #{line.inspect}") unless md
158
+ frame.abs_path = md[1]
159
+ frame.lineno = md[2].to_i
160
+ frame.function = md[3] if md[3]
161
+ frame.filename = strip_load_path_from(frame.abs_path)
162
+ if context_lines = @configuration[:context_lines]
163
+ frame.pre_context, frame.context_line, frame.post_context = \
164
+ get_context(frame.abs_path, frame.lineno, context_lines)
165
+ end
166
+ frame
167
+ end
168
+
148
169
  # For cross-language compat
149
170
  class << self
150
171
  alias :captionException :capture_exception
172
+ alias :captureMessage :capture_message
151
173
  end
152
174
 
153
175
  private
@@ -156,6 +178,16 @@ module Raven
156
178
  def self._source_lines(path, from, to)
157
179
  end
158
180
 
159
- end
181
+ def get_context(path, line, context)
182
+ lines = (2 * context + 1).times.map do |i|
183
+ Raven::LineCache::getline(path, line - context + i)
184
+ end
185
+ [lines[0..(context-1)], lines[context], lines[(context+1)..-1]]
186
+ end
160
187
 
188
+ def strip_load_path_from(path)
189
+ prefix = $:.select {|s| path.start_with?(s)}.sort_by {|s| s.length}.last
190
+ prefix ? path[prefix.chomp(File::SEPARATOR).length+1..-1] : path
191
+ end
192
+ end
161
193
  end
@@ -10,8 +10,14 @@ module Raven
10
10
  property :data
11
11
  property :query_string
12
12
  property :cookies
13
- property :headers, :default => {}
14
- property :env, :default => {}
13
+ property :headers
14
+ property :env
15
+
16
+ def initialize(*arguments)
17
+ self.headers = {}
18
+ self.env = {}
19
+ super(*arguments)
20
+ end
15
21
 
16
22
  def from_rack(env)
17
23
  require 'rack'
@@ -6,8 +6,12 @@ module Raven
6
6
 
7
7
  name 'sentry.interfaces.Message'
8
8
  property :message, :required => true
9
- property :params, :default => []
9
+ property :params
10
10
 
11
+ def initialize(*arguments)
12
+ self.params = []
13
+ super(*arguments)
14
+ end
11
15
  end
12
16
 
13
17
  register_interface :message => MessageInterface
@@ -9,6 +9,11 @@ module Raven
9
9
  name 'sentry.interfaces.Stacktrace'
10
10
  property :frames, :default => []
11
11
 
12
+ def initialize(*arguments)
13
+ self.frames = []
14
+ super(*arguments)
15
+ end
16
+
12
17
  def to_hash
13
18
  data = super
14
19
  data['frames'] = data['frames'].map{|frame| frame.to_hash}
@@ -24,12 +29,19 @@ module Raven
24
29
  property :abs_path
25
30
  property :filename, :required => true
26
31
  property :function
27
- property :vars, :default => {}
28
- property :pre_context, :default => []
29
- property :post_context, :default => []
32
+ property :vars
33
+ property :pre_context
34
+ property :post_context
30
35
  property :context_line
31
36
  property :lineno, :required => true
32
37
 
38
+ def initialize(*arguments)
39
+ self.vars= {}
40
+ self.pre_context = []
41
+ self.post_context = []
42
+ super(*arguments)
43
+ end
44
+
33
45
  def to_hash
34
46
  data = super
35
47
  data.delete('vars') unless self.vars && !self.vars.empty?
@@ -0,0 +1,15 @@
1
+ module Raven
2
+
3
+ module Processor
4
+ class Processor
5
+ def initialize(client)
6
+ @client = client
7
+ end
8
+
9
+ def process(data)
10
+ data
11
+ end
12
+ end
13
+ end
14
+
15
+ end
@@ -0,0 +1,43 @@
1
+ require 'raven/processor'
2
+
3
+ module Raven
4
+ module Processor
5
+ class SanitizeData < Processor
6
+
7
+ MASK = '********'
8
+ FIELDS_RE = /(authorization|password|passwd|secret)/i
9
+ VALUES_RE = /^\d{16}$/
10
+
11
+ def apply(value, key=nil, &block)
12
+ if value.is_a?(Hash)
13
+ value.each.inject({}) do |memo, (k, v)|
14
+ memo[k] = apply(v, k, &block)
15
+ memo
16
+ end
17
+ elsif value.is_a?(Array)
18
+ value.map do |value|
19
+ apply(value, key, &block)
20
+ end
21
+ else
22
+ block.call(key, value)
23
+ end
24
+ end
25
+
26
+ def sanitize(key, value)
27
+ if !value || value.empty?
28
+ value
29
+ elsif VALUES_RE.match(value) or FIELDS_RE.match(key)
30
+ MASK
31
+ else
32
+ value
33
+ end
34
+ end
35
+
36
+ def process(data)
37
+ apply(data) do |key, value|
38
+ sanitize(key, value)
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
data/lib/raven/rack.rb CHANGED
@@ -29,12 +29,12 @@ module Raven
29
29
  raise # Don't capture Raven errors
30
30
  rescue Exception => e
31
31
  evt = Event.capture_rack_exception(e, env)
32
- Raven.send(evt) if evt
32
+ Raven.send(evt)
33
33
  raise
34
34
  end
35
35
 
36
36
  if env['rack.exception']
37
- evt = Event.capture_rack_exception(e, env)
37
+ evt = Event.capture_rack_exception(env['rack.exception'], env)
38
38
  Raven.send(evt) if evt
39
39
  end
40
40
 
data/lib/raven/railtie.rb CHANGED
@@ -4,7 +4,7 @@ require 'rails'
4
4
  module Raven
5
5
  class Railtie < ::Rails::Railtie
6
6
  initializer "raven.use_rack_middleware" do |app|
7
- app.config.middleware.use "Raven::Rack" unless defined?(::ActionDispatch::DebugExceptions)
7
+ app.config.middleware.insert 0, "Raven::Rack"
8
8
  end
9
9
 
10
10
  config.after_initialize do
@@ -15,6 +15,9 @@ module Raven
15
15
  if defined?(::ActionDispatch::DebugExceptions)
16
16
  require 'raven/rails/middleware/debug_exceptions_catcher'
17
17
  ::ActionDispatch::DebugExceptions.send(:include, Raven::Rails::Middleware::DebugExceptionsCatcher)
18
+ elsif defined?(::ActionDispatch::ShowExceptions)
19
+ require 'raven/rails/middleware/debug_exceptions_catcher'
20
+ ::ActionDispatch::ShowExceptions.send(:include, Raven::Rails::Middleware::DebugExceptionsCatcher)
18
21
  end
19
22
  end
20
23
  end
data/lib/raven/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Raven
2
- VERSION = "0.2"
2
+ VERSION = "0.3"
3
3
  end
metadata CHANGED
@@ -1,8 +1,11 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sentry-raven
3
3
  version: !ruby/object:Gem::Version
4
- prerelease:
5
- version: "0.2"
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 3
8
+ version: "0.3"
6
9
  platform: ruby
7
10
  authors:
8
11
  - Noah Kantrowitz
@@ -10,16 +13,21 @@ autorequire:
10
13
  bindir: bin
11
14
  cert_chain: []
12
15
 
13
- date: 2012-07-14 00:00:00 Z
16
+ date: 2012-10-26 00:00:00 -07:00
17
+ default_executable:
14
18
  dependencies:
15
19
  - !ruby/object:Gem::Dependency
16
20
  name: faraday
17
21
  prerelease: false
18
22
  requirement: &id001 !ruby/object:Gem::Requirement
19
- none: false
20
23
  requirements:
21
24
  - - ~>
22
25
  - !ruby/object:Gem::Version
26
+ segments:
27
+ - 0
28
+ - 8
29
+ - 0
30
+ - rc2
23
31
  version: 0.8.0.rc2
24
32
  type: :runtime
25
33
  version_requirements: *id001
@@ -27,32 +35,36 @@ dependencies:
27
35
  name: uuidtools
28
36
  prerelease: false
29
37
  requirement: &id002 !ruby/object:Gem::Requirement
30
- none: false
31
38
  requirements:
32
39
  - - ">="
33
40
  - !ruby/object:Gem::Version
41
+ segments:
42
+ - 0
34
43
  version: "0"
35
44
  type: :runtime
36
45
  version_requirements: *id002
37
46
  - !ruby/object:Gem::Dependency
38
- name: yajl-ruby
47
+ name: multi_json
39
48
  prerelease: false
40
49
  requirement: &id003 !ruby/object:Gem::Requirement
41
- none: false
42
50
  requirements:
43
- - - ">="
51
+ - - ~>
44
52
  - !ruby/object:Gem::Version
45
- version: "0"
53
+ segments:
54
+ - 1
55
+ - 0
56
+ version: "1.0"
46
57
  type: :runtime
47
58
  version_requirements: *id003
48
59
  - !ruby/object:Gem::Dependency
49
60
  name: hashie
50
61
  prerelease: false
51
62
  requirement: &id004 !ruby/object:Gem::Requirement
52
- none: false
53
63
  requirements:
54
64
  - - ">="
55
65
  - !ruby/object:Gem::Version
66
+ segments:
67
+ - 0
56
68
  version: "0"
57
69
  type: :runtime
58
70
  version_requirements: *id004
@@ -77,6 +89,8 @@ files:
77
89
  - lib/raven/interfaces.rb
78
90
  - lib/raven/linecache.rb
79
91
  - lib/raven/logger.rb
92
+ - lib/raven/processor.rb
93
+ - lib/raven/processors/sanitizedata.rb
80
94
  - lib/raven/rack.rb
81
95
  - lib/raven/rails/middleware/debug_exceptions_catcher.rb
82
96
  - lib/raven/railtie.rb
@@ -84,7 +98,8 @@ files:
84
98
  - lib/raven.rb
85
99
  - README.md
86
100
  - LICENSE
87
- homepage: http://github.com/coderanger/raven-ruby
101
+ has_rdoc: true
102
+ homepage: http://github.com/getsentry/raven-ruby
88
103
  licenses: []
89
104
 
90
105
  post_install_message:
@@ -93,21 +108,23 @@ rdoc_options: []
93
108
  require_paths:
94
109
  - lib
95
110
  required_ruby_version: !ruby/object:Gem::Requirement
96
- none: false
97
111
  requirements:
98
112
  - - ">="
99
113
  - !ruby/object:Gem::Version
114
+ segments:
115
+ - 0
100
116
  version: "0"
101
117
  required_rubygems_version: !ruby/object:Gem::Requirement
102
- none: false
103
118
  requirements:
104
119
  - - ">="
105
120
  - !ruby/object:Gem::Version
121
+ segments:
122
+ - 0
106
123
  version: "0"
107
124
  requirements: []
108
125
 
109
126
  rubyforge_project:
110
- rubygems_version: 1.8.24
127
+ rubygems_version: 1.3.6
111
128
  signing_key:
112
129
  specification_version: 3
113
130
  summary: A gem that provides a client interface for the Sentry error logger