sentry-raven 0.2 → 0.3

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.

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