sentry-raven 0.12.3 → 0.13.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f4d1ff44aab9878c7070e85ac6bac6a2573e5332
4
- data.tar.gz: 7668ff7bdc645dc3d77305f0be63bd028696eee5
3
+ metadata.gz: c09fc2d9d439ff50aa3d575716890797ebb81cb6
4
+ data.tar.gz: 834dc9c9d24c2bf004f071b9ec77d3c96edb22e7
5
5
  SHA512:
6
- metadata.gz: ba3fb97a2c6a77adf1e8d5afcf00d7d94f14b3d5bf3851db165e0624afc1aa702efd61d2c3f24cf9195b8b9e2bedf72244219f7bd61a698e8d443732e227efec
7
- data.tar.gz: e96788e536488bed998d179c9c752157044c5a703de5c479c9265966bde529f6d37dcba908478e9a292e4c149256f1ecb67ab748c2e89829ae73c8496c8d7e4a
6
+ metadata.gz: b07c730db8e117c99f3f3e40bbf66863880bb630b2e57de211335994749a930f3f9ea4a86b49eb3f632a88c09184588c0d38b73ce9f0fb3482845c4ccfe4f904
7
+ data.tar.gz: 3e3390603c2835ff88c0644c753a3459f721026adc5bb0f03e207158f65528e861363c571d6a73c7185b0f844fdfdb60190b31fe0ed6e6db3e5bcc7363e69b85
data/README.md CHANGED
@@ -2,13 +2,12 @@
2
2
 
3
3
  [![Gem Version](https://img.shields.io/gem/v/sentry-raven.svg)](https://rubygems.org/gems/sentry-raven)
4
4
  [![Build Status](https://img.shields.io/travis/getsentry/raven-ruby/master.svg)](https://travis-ci.org/getsentry/raven-ruby)
5
- [![Coverage Status](https://img.shields.io/coveralls/getsentry/raven-ruby/master.svg)](https://coveralls.io/r/getsentry/raven-ruby)
6
5
 
7
6
  A client and integration layer for the [Sentry](https://github.com/getsentry/sentry) error reporting API.
8
7
 
9
8
  ## Requirements
10
9
 
11
- We test on Ruby MRI 1.8.7/REE, 1.9.3, 2.0 and 2.1. JRuby support is experimental - check TravisCI to see if the build is passing or failing.
10
+ We test on Ruby MRI 1.8.7/REE, 1.9.3, 2.0, 2.1 and 2.2. JRuby support is experimental - check TravisCI to see if the build is passing or failing.
12
11
 
13
12
  ## Getting Started
14
13
  ### Install
@@ -45,7 +44,7 @@ end
45
44
 
46
45
  ## More Information
47
46
 
48
- Full documentation and more information on advanced configuration, sending more information, scrubbing sensitive data, and more can be found on [the wiki](https://github.com/getsentry/raven-ruby/wiki).
47
+ Full documentation and more information on advanced configuration, sending more information, scrubbing sensitive data, and more can be found on [the wiki](https://github.com/getsentry/raven-ruby/wiki).
49
48
 
50
49
  * [Documentation](https://github.com/getsentry/raven-ruby/wiki)
51
50
  * [Bug Tracker](http://github.com/getsentry/raven-ruby/issues>)
data/lib/raven/base.rb CHANGED
@@ -7,6 +7,7 @@ require 'raven/event'
7
7
  require 'raven/logger'
8
8
  require 'raven/interfaces/message'
9
9
  require 'raven/interfaces/exception'
10
+ require 'raven/interfaces/single_exception'
10
11
  require 'raven/interfaces/stack_trace'
11
12
  require 'raven/interfaces/http'
12
13
  require 'raven/processor'
@@ -49,9 +50,15 @@ module Raven
49
50
  end
50
51
 
51
52
  # Tell the log that the client is good to go
52
- def report_ready
53
- self.logger.info "Raven #{VERSION} ready to catch errors"
53
+ def report_status
54
+ return if client.configuration.silence_ready
55
+ if client.configuration.send_in_current_environment?
56
+ logger.info "Raven #{VERSION} ready to catch errors"
57
+ else
58
+ logger.info "Raven #{VERSION} configured not to send errors."
59
+ end
54
60
  end
61
+ alias_method :report_ready, :report_status
55
62
 
56
63
  # Call this method to modify defaults in your initializers.
57
64
  #
@@ -59,11 +66,11 @@ module Raven
59
66
  # Raven.configure do |config|
60
67
  # config.server = 'http://...'
61
68
  # end
62
- def configure(silent = false)
69
+ def configure
63
70
  yield(configuration) if block_given?
64
71
 
65
72
  self.client = Client.new(configuration)
66
- report_ready unless silent
73
+ report_status
67
74
  self.client
68
75
  end
69
76
 
@@ -83,65 +90,39 @@ module Raven
83
90
  # Raven.capture do
84
91
  # MyApp.run
85
92
  # end
86
- def capture(options = {}, &block)
87
- if block
88
- begin
89
- block.call
90
- rescue Error
91
- raise # Don't capture Raven errors
92
- rescue Exception => e
93
- capture_exception(e, options)
94
- raise
95
- end
96
- else
97
- # Install at_exit hook
98
- at_exit do
99
- if $ERROR_INFO
100
- logger.debug "Caught a post-mortem exception: #{$ERROR_INFO.inspect}"
101
- capture_exception($ERROR_INFO, options)
102
- end
103
- end
93
+ def capture(options = {})
94
+ install_at_exit_hook unless block_given?
95
+ begin
96
+ yield
97
+ rescue Error
98
+ raise # Don't capture Raven errors
99
+ rescue Exception => e
100
+ capture_exception(e, options)
101
+ raise
104
102
  end
105
103
  end
106
104
 
107
- def capture_exception(exception, options = {})
108
- send_or_skip(exception) do
109
- if (evt = Event.from_exception(exception, options))
110
- yield evt if block_given?
111
- if configuration.async?
112
- configuration.async.call(evt)
113
- else
114
- send(evt)
115
- end
105
+ def capture_type(obj, options = {})
106
+ return false unless should_capture?(obj)
107
+ message_or_exc = obj.is_a?(String) ? "message" : "exception"
108
+ if (evt = Event.send("from_" + message_or_exc, obj, options))
109
+ yield evt if block_given?
110
+ if configuration.async?
111
+ configuration.async.call(evt)
112
+ else
113
+ send(evt)
116
114
  end
117
115
  end
118
116
  end
117
+ alias_method :capture_message, :capture_type
118
+ alias_method :capture_exception, :capture_type
119
119
 
120
- def capture_message(message, options = {})
121
- send_or_skip(message) do
122
- if (evt = Event.from_message(message, options))
123
- yield evt if block_given?
124
- if configuration.async?
125
- configuration.async.call(evt)
126
- else
127
- send(evt)
128
- end
129
- end
130
- end
131
- end
132
-
133
- def send_or_skip(exc)
134
- should_send = if configuration.should_send
135
- configuration.should_send.call(*[exc])
120
+ def should_capture?(message_or_exc)
121
+ if configuration.should_capture
122
+ configuration.should_capture.call(*[message_or_exc])
136
123
  else
137
124
  true
138
125
  end
139
-
140
- if configuration.send_in_current_environment? && should_send
141
- yield if block_given?
142
- else
143
- configuration.log_excluded_environment_message
144
- end
145
126
  end
146
127
 
147
128
  # Provides extra context to the exception prior to it being handled by
@@ -231,5 +212,14 @@ module Raven
231
212
  alias :captureMessage :capture_message
232
213
  alias :annotateException :annotate_exception
233
214
  alias :annotate :annotate_exception
215
+
216
+ private
217
+
218
+ def install_at_exit_hook
219
+ at_exit do
220
+ logger.debug "Caught a post-mortem exception: #{$ERROR_INFO.inspect}"
221
+ capture_exception($ERROR_INFO, options)
222
+ end
223
+ end
234
224
  end
235
225
  end
data/lib/raven/client.rb CHANGED
@@ -7,9 +7,8 @@ require 'raven/transports/http'
7
7
  require 'raven/transports/udp'
8
8
 
9
9
  module Raven
10
-
10
+ # Encodes events and sends them to the Sentry server.
11
11
  class Client
12
-
13
12
  PROTOCOL_VERSION = '5'
14
13
  USER_AGENT = "raven-ruby/#{Raven::VERSION}"
15
14
  CONTENT_TYPE = 'application/json'
@@ -23,22 +22,20 @@ module Raven
23
22
  end
24
23
 
25
24
  def send(event)
26
- unless configuration.send_in_current_environment?
27
- configuration.log_excluded_environment_message
28
- return
29
- end
25
+ return false unless configuration_allows_sending
30
26
 
31
- # Set the project ID correctly
32
- event.project = self.configuration.project_id
27
+ # Convert to hash
28
+ event = event.to_hash
33
29
 
34
30
  if !@state.should_try?
35
31
  Raven.logger.error("Not sending event due to previous failure(s): #{get_log_message(event)}")
36
32
  return
37
33
  end
38
34
 
39
- Raven.logger.debug "Sending event #{event.id} to Sentry"
35
+ Raven.logger.debug "Sending event #{event['id']} to Sentry"
40
36
 
41
37
  content_type, encoded_data = encode(event)
38
+
42
39
  begin
43
40
  transport.send(generate_auth_header, encoded_data,
44
41
  :content_type => content_type)
@@ -54,39 +51,40 @@ module Raven
54
51
 
55
52
  private
56
53
 
57
- def encode(event)
58
- hash = event.to_hash
59
-
60
- # apply processors
61
- hash = @processors.reduce(hash) do |memo, processor|
62
- processor.process(memo)
54
+ def configuration_allows_sending
55
+ if configuration.send_in_current_environment?
56
+ true
57
+ else
58
+ configuration.log_excluded_environment_message
59
+ false
63
60
  end
61
+ end
64
62
 
63
+ def encode(event)
64
+ hash = @processors.reduce(event.to_hash) { |memo, p| p.process(memo) }
65
65
  encoded = OkJson.encode(hash)
66
66
 
67
- case self.configuration.encoding
67
+ case configuration.encoding
68
68
  when 'gzip'
69
- gzipped = Zlib::Deflate.deflate(encoded)
70
- b64_encoded = strict_encode64(gzipped)
71
- return 'application/octet-stream', b64_encoded
69
+ ['application/octet-stream', strict_encode64(Zlib::Deflate.deflate(encoded))]
72
70
  else
73
- return 'application/json', encoded
71
+ ['application/json', encoded]
74
72
  end
75
73
  end
76
74
 
77
75
  def get_log_message(event)
78
- (event && event.message) || '<no message value>'
76
+ (event && event['message']) || '<no message value>'
79
77
  end
80
78
 
81
79
  def transport
82
80
  @transport ||=
83
- case self.configuration.scheme
81
+ case configuration.scheme
84
82
  when 'udp'
85
- Transports::UDP.new self.configuration
83
+ Transports::UDP.new(configuration)
86
84
  when 'http', 'https'
87
- Transports::HTTP.new self.configuration
85
+ Transports::HTTP.new(configuration)
88
86
  else
89
- raise Error.new("Unknown transport scheme '#{self.configuration.scheme}'")
87
+ raise Error, "Unknown transport scheme '#{self.configuration.scheme}'"
90
88
  end
91
89
  end
92
90
 
@@ -96,14 +94,12 @@ module Raven
96
94
  'sentry_version' => PROTOCOL_VERSION,
97
95
  'sentry_client' => USER_AGENT,
98
96
  'sentry_timestamp' => now,
99
- 'sentry_key' => self.configuration.public_key,
100
- 'sentry_secret' => self.configuration.secret_key,
97
+ 'sentry_key' => configuration.public_key,
98
+ 'sentry_secret' => configuration.secret_key
101
99
  }
102
100
  'Sentry ' + fields.map { |key, value| "#{key}=#{value}" }.join(', ')
103
101
  end
104
102
 
105
- private
106
-
107
103
  def strict_encode64(string)
108
104
  if Base64.respond_to? :strict_encode64
109
105
  Base64.strict_encode64 string
@@ -31,6 +31,9 @@ module Raven
31
31
  # Logger to use internally
32
32
  attr_accessor :logger
33
33
 
34
+ # Silence ready message
35
+ attr_accessor :silence_ready
36
+
34
37
  # Number of lines of code context to capture, or nil for none
35
38
  attr_accessor :context_lines
36
39
 
@@ -68,6 +71,8 @@ module Raven
68
71
 
69
72
  attr_accessor :server_name
70
73
 
74
+ attr_accessor :release
75
+
71
76
  # DEPRECATED: This option is now ignored as we use our own adapter.
72
77
  attr_accessor :json_adapter
73
78
 
@@ -84,8 +89,8 @@ module Raven
84
89
  # ActionDispatch::ShowExceptions or ActionDispatch::DebugExceptions
85
90
  attr_accessor :catch_debugged_exceptions
86
91
 
87
- # Provide a configurable callback to block or send events
88
- attr_accessor :should_send
92
+ # Provide a configurable callback to determine event capture
93
+ attr_accessor :should_capture
89
94
 
90
95
  # additional fields to sanitize
91
96
  attr_accessor :sanitize_fields
data/lib/raven/event.rb CHANGED
@@ -23,52 +23,46 @@ module Raven
23
23
  PLATFORM = "ruby"
24
24
 
25
25
  attr_reader :id
26
- attr_accessor :project, :message, :timestamp, :time_spent, :level
27
- attr_accessor :logger, :culprit, :server_name, :modules, :extra, :tags
28
-
29
- def initialize(options = {}, &block)
30
- @configuration = options[:configuration] || Raven.configuration
31
- @interfaces = {}
32
-
33
- context = options[:context] || Raven.context
34
-
35
- @id = options[:id] || generate_event_id
36
- @message = options[:message]
37
- @timestamp = options[:timestamp] || Time.now.utc
38
- @time_spent = options[:time_spent]
39
-
40
- @level = options[:level] || :error
41
- @logger = options[:logger] || 'root'
42
- @culprit = options[:culprit]
43
- @server_name = options[:server_name] || @configuration.server_name || get_hostname
44
-
45
- options[:modules] ||= get_modules if @configuration.send_modules
46
-
47
- @modules = options[:modules]
48
-
49
- @user = options[:user] || {}
50
- @user.merge!(context.user)
26
+ attr_accessor :project, :message, :timestamp, :time_spent, :level, :logger,
27
+ :culprit, :server_name, :release, :modules, :extra, :tags, :context, :configuration
28
+
29
+ def initialize(init = {})
30
+ @configuration = Raven.configuration
31
+ @interfaces = {}
32
+ @context = Raven.context
33
+ @id = generate_event_id
34
+ @message = nil
35
+ @timestamp = Time.now.utc
36
+ @time_spent = nil
37
+ @level = :error
38
+ @logger = 'root'
39
+ @culprit = nil
40
+ @server_name = @configuration.server_name || get_hostname
41
+ @release = @configuration.release
42
+ @modules = get_modules if @configuration.send_modules
43
+ @user = {}
44
+ @extra = {}
45
+ @tags = {}
46
+
47
+ yield self if block_given?
48
+
49
+ if !self[:http] && @context.rack_env
50
+ interface :http do |int|
51
+ int.from_rack(@context.rack_env)
52
+ end
53
+ end
51
54
 
52
- @extra = options[:extra] || {}
53
- @extra.merge!(context.extra)
55
+ init.each_pair { |key, val| instance_variable_set('@' + key.to_s, val) }
54
56
 
55
- @tags = {}
57
+ @user.merge!(@context.user)
58
+ @extra.merge!(@context.extra)
56
59
  @tags.merge!(@configuration.tags)
57
- @tags.merge!(options[:tags] || {})
58
- @tags.merge!(context.tags)
59
-
60
- block.call(self) if block
61
-
62
- if !self[:http] && context.rack_env
63
- self.interface :http do |int|
64
- int.from_rack(context.rack_env)
65
- end
66
- end
60
+ @tags.merge!(@context.tags)
67
61
 
68
62
  # Some type coercion
69
- @timestamp = @timestamp.strftime('%Y-%m-%dT%H:%M:%S') if @timestamp.is_a?(Time)
63
+ @timestamp = @timestamp.strftime('%Y-%m-%dT%H:%M:%S') if @timestamp.is_a?(Time)
70
64
  @time_spent = (@time_spent*1000).to_i if @time_spent.is_a?(Float)
71
- @level = LOG_LEVELS[@level.to_s.downcase] if @level.is_a?(String) || @level.is_a?(Symbol)
65
+ @level = LOG_LEVELS[@level.to_s.downcase] if @level.is_a?(String) || @level.is_a?(Symbol)
72
66
  end
73
67
 
74
68
  def get_hostname
@@ -99,23 +93,24 @@ module Raven
99
93
 
100
94
  def to_hash
101
95
  data = {
102
- 'event_id' => @id,
103
- 'message' => @message,
104
- 'timestamp' => @timestamp,
105
- 'time_spent' => @time_spent,
106
- 'level' => @level,
107
- 'project' => @project,
108
- 'logger' => @logger,
109
- 'platform' => PLATFORM,
96
+ :event_id => @id,
97
+ :message => @message,
98
+ :timestamp => @timestamp,
99
+ :time_spent => @time_spent,
100
+ :level => @level,
101
+ :project => @project,
102
+ :logger => @logger,
103
+ :platform => PLATFORM,
110
104
  }
111
- data['culprit'] = @culprit if @culprit
112
- data['server_name'] = @server_name if @server_name
113
- data['modules'] = @modules if @modules
114
- data['extra'] = @extra if @extra
115
- data['tags'] = @tags if @tags
116
- data['user'] = @user if @user
105
+ data[:culprit] = @culprit if @culprit
106
+ data[:server_name] = @server_name if @server_name
107
+ data[:release] = @release if @release
108
+ data[:modules] = @modules if @modules
109
+ data[:extra] = @extra if @extra
110
+ data[:tags] = @tags if @tags
111
+ data[:user] = @user if @user
117
112
  @interfaces.each_pair do |name, int_data|
118
- data[name] = int_data.to_hash
113
+ data[name.to_sym] = int_data.to_hash
119
114
  end
120
115
  data
121
116
  end
@@ -135,54 +130,78 @@ module Raven
135
130
  return nil
136
131
  end
137
132
 
138
- context_lines = configuration[:context_lines]
139
-
140
133
  new(options) do |evt|
134
+ evt.configuration = configuration
141
135
  evt.message = "#{exc.class}: #{exc.message}"
142
136
  evt.level = options[:level] || :error
143
137
 
144
- evt.interface(:exception) do |int|
145
- int.type = exc.class.to_s
146
- int.value = exc.to_s
147
- int.module = exc.class.to_s.split('::')[0...-1].join('::')
148
-
149
- # TODO(dcramer): this needs cleaned up, but I couldn't figure out how to
150
- # work Hashie as a non-Rubyist
151
- if exc.backtrace
152
- int.stacktrace = StacktraceInterface.new do |stacktrace|
153
- backtrace = Backtrace.parse(exc.backtrace)
154
- stacktrace.frames = backtrace.lines.reverse.map do |line|
155
- stacktrace.frame do |frame|
156
- frame.abs_path = line.file
157
- frame.function = line.method
158
- frame.lineno = line.number
159
- frame.in_app = line.in_app
160
- if context_lines && frame.abs_path
161
- frame.pre_context, frame.context_line, frame.post_context = \
162
- evt.get_file_context(frame.abs_path, frame.lineno, context_lines)
163
- end
164
- end
165
- end.select { |f| f.filename }
166
-
167
- evt.culprit = evt.get_culprit(stacktrace.frames)
168
- end
169
- end
170
- end
138
+ add_exception_interface(evt, exc)
171
139
 
172
140
  block.call(evt) if block
173
141
  end
174
142
  end
175
143
 
176
144
  def self.from_message(message, options = {})
145
+ configuration = options[:configuration] || Raven.configuration
177
146
  new(options) do |evt|
147
+ evt.configuration = configuration
178
148
  evt.message = message
179
149
  evt.level = options[:level] || :error
180
150
  evt.interface :message do |int|
181
151
  int.message = message
182
152
  end
153
+ if options[:backtrace]
154
+ evt.interface(:stacktrace) do |int|
155
+ stacktrace_interface_from(int, evt, options[:backtrace])
156
+ end
157
+ end
158
+ end
159
+ end
160
+
161
+ def self.add_exception_interface(evt, exc)
162
+ evt.interface(:exception) do |exc_int|
163
+ exceptions = [exc]
164
+ while exc.respond_to?(:cause) && exc.cause
165
+ exceptions << exc.cause
166
+ exc = exc.cause
167
+ end
168
+ exceptions.reverse!
169
+
170
+ exc_int.values = exceptions.map do |exc|
171
+ SingleExceptionInterface.new do |int|
172
+ int.type = exc.class.to_s
173
+ int.value = exc.to_s
174
+ int.module = exc.class.to_s.split('::')[0...-1].join('::')
175
+
176
+ int.stacktrace = if exc.backtrace
177
+ StacktraceInterface.new do |stacktrace|
178
+ stacktrace_interface_from(stacktrace, evt, exc.backtrace)
179
+ end
180
+ end
181
+ end
182
+ end
183
183
  end
184
184
  end
185
185
 
186
+ def self.stacktrace_interface_from(int, evt, backtrace)
187
+ backtrace = Backtrace.parse(backtrace)
188
+ int.frames = backtrace.lines.reverse.map do |line|
189
+ StacktraceInterface::Frame.new.tap do |frame|
190
+ frame.abs_path = line.file
191
+ frame.function = line.method
192
+ frame.lineno = line.number
193
+ frame.in_app = line.in_app
194
+
195
+ if evt.configuration[:context_lines] && frame.abs_path
196
+ frame.pre_context, frame.context_line, frame.post_context = \
197
+ evt.get_file_context(frame.abs_path, frame.lineno, evt.configuration[:context_lines])
198
+ end
199
+ end
200
+ end.select { |f| f.filename }
201
+
202
+ evt.culprit = evt.get_culprit(int.frames)
203
+ end
204
+
186
205
  # Because linecache can go to hell
187
206
  def self._source_lines(_path, _from, _to)
188
207
  end
@@ -22,26 +22,19 @@ module Raven
22
22
  # Use a standard Raven.configure call to configure your server credentials.
23
23
  class Rack
24
24
 
25
- def self.capture_exception(exception, env, options = {})
25
+ def self.capture_type(exception, env, options = {})
26
26
  if env['requested_at']
27
27
  options[:time_spent] = Time.now - env['requested_at']
28
28
  end
29
- Raven.capture_exception(exception, options) do |evt|
29
+ Raven.capture_type(exception, options) do |evt|
30
30
  evt.interface :http do |int|
31
31
  int.from_rack(env)
32
32
  end
33
33
  end
34
34
  end
35
-
36
- def self.capture_message(message, env, options = {})
37
- if env['requested_at']
38
- options[:time_spent] = Time.now - env['requested_at']
39
- end
40
- Raven.capture_message(message, options) do |evt|
41
- evt.interface :http do |int|
42
- int.from_rack(env)
43
- end
44
- end
35
+ class << self
36
+ alias_method :capture_message, :capture_type
37
+ alias_method :capture_exception, :capture_type
45
38
  end
46
39
 
47
40
  def initialize(app)
@@ -15,7 +15,7 @@ module Raven
15
15
  end
16
16
 
17
17
  config.after_initialize do
18
- Raven.configure(true) do |config|
18
+ Raven.configure do |config|
19
19
  config.logger ||= ::Rails.logger
20
20
  config.project_root ||= ::Rails.root
21
21
  end
@@ -1,16 +1,11 @@
1
- require 'raven/better_attr_accessor'
2
-
3
1
  module Raven
4
2
 
5
3
  INTERFACES = {}
6
4
 
7
5
  class Interface
8
- include BetterAttrAccessor
9
- alias_method :to_hash, :attributes
10
-
11
6
  def initialize(attributes = nil)
12
7
  attributes.each do |attr, value|
13
- send "#{attr}=", value
8
+ public_send "#{attr}=", value
14
9
  end if attributes
15
10
 
16
11
  yield self if block_given?
@@ -19,6 +14,10 @@ module Raven
19
14
  def self.name(value = nil)
20
15
  @interface_name ||= value
21
16
  end
17
+
18
+ def to_hash
19
+ Hash[instance_variables.map { |name| [name[1..-1].to_sym, instance_variable_get(name)] } ]
20
+ end
22
21
  end
23
22
 
24
23
  def self.register_interface(mapping)
@@ -4,15 +4,12 @@ module Raven
4
4
  class ExceptionInterface < Interface
5
5
 
6
6
  name 'exception'
7
- attr_accessor :type
8
- attr_accessor :value
9
- attr_accessor :module
10
- attr_accessor :stacktrace
7
+ attr_accessor :values
11
8
 
12
9
  def to_hash(*args)
13
10
  data = super(*args)
14
- if data['stacktrace']
15
- data['stacktrace'] = data['stacktrace'].to_hash
11
+ if data[:values]
12
+ data[:values] = data[:values].map(&:to_hash)
16
13
  end
17
14
  data
18
15
  end
@@ -15,6 +15,7 @@ module Raven
15
15
  def initialize(*arguments)
16
16
  self.headers = {}
17
17
  self.env = {}
18
+ self.cookies = nil
18
19
  super(*arguments)
19
20
  end
20
21
 
@@ -0,0 +1,19 @@
1
+ require 'raven/interfaces'
2
+
3
+ module Raven
4
+ class SingleExceptionInterface < Interface
5
+
6
+ attr_accessor :type
7
+ attr_accessor :value
8
+ attr_accessor :module
9
+ attr_accessor :stacktrace
10
+
11
+ def to_hash(*args)
12
+ data = super(*args)
13
+ if data[:stacktrace]
14
+ data[:stacktrace] = data[:stacktrace].to_hash
15
+ end
16
+ data
17
+ end
18
+ end
19
+ end
@@ -4,7 +4,7 @@ module Raven
4
4
  class StacktraceInterface < Interface
5
5
 
6
6
  name 'stacktrace'
7
- attr_accessor :frames, :default => []
7
+ attr_accessor :frames
8
8
 
9
9
  def initialize(*arguments)
10
10
  self.frames = []
@@ -13,39 +13,49 @@ module Raven
13
13
 
14
14
  def to_hash(*args)
15
15
  data = super(*args)
16
- data['frames'] = data['frames'].map { |frame| frame.to_hash }
16
+ data[:frames] = data[:frames].map { |frame| frame.to_hash }
17
17
  data
18
18
  end
19
19
 
20
- def frame(attributes = nil, &block)
21
- Frame.new(attributes, &block)
22
- end
23
-
24
20
  # Not actually an interface, but I want to use the same style
25
21
  class Frame < Interface
26
22
  attr_accessor :abs_path
27
23
  attr_accessor :function
28
- attr_accessor :vars, :default => []
29
- attr_accessor :pre_context, :default => []
30
- attr_accessor :post_context, :default => []
24
+ attr_accessor :vars
25
+ attr_accessor :pre_context
26
+ attr_accessor :post_context
31
27
  attr_accessor :context_line
32
28
  attr_accessor :lineno
33
29
  attr_accessor :in_app
34
30
 
31
+ def initialize(*arguments)
32
+ self.vars, self.pre_context, self.post_context = [], [], []
33
+ super(*arguments)
34
+ end
35
+
35
36
  def filename
36
37
  return nil if self.abs_path.nil?
37
38
 
38
- prefix = $LOAD_PATH.select { |s| self.abs_path.start_with?(s.to_s) }.sort_by { |s| s.to_s.length }.last
39
+ prefix = if project_root && self.abs_path.start_with?(project_root)
40
+ project_root
41
+ else
42
+ $LOAD_PATH.select { |s| self.abs_path.start_with?(s.to_s) }.sort_by { |s| s.to_s.length }.last
43
+ end
44
+
39
45
  prefix ? self.abs_path[prefix.to_s.chomp(File::SEPARATOR).length+1..-1] : self.abs_path
40
46
  end
41
47
 
48
+ def project_root
49
+ @project_root ||= Raven.configuration.project_root && Raven.configuration.project_root.to_s
50
+ end
51
+
42
52
  def to_hash(*args)
43
53
  data = super(*args)
44
- data['filename'] = self.filename
45
- data.delete('vars') unless self.vars && !self.vars.empty?
46
- data.delete('pre_context') unless self.pre_context && !self.pre_context.empty?
47
- data.delete('post_context') unless self.post_context && !self.post_context.empty?
48
- data.delete('context_line') unless self.context_line && !self.context_line.empty?
54
+ data[:filename] = self.filename
55
+ data.delete(:vars) unless self.vars && !self.vars.empty?
56
+ data.delete(:pre_context) unless self.pre_context && !self.pre_context.empty?
57
+ data.delete(:post_context) unless self.post_context && !self.post_context.empty?
58
+ data.delete(:context_line) unless self.context_line && !self.context_line.empty?
49
59
  data
50
60
  end
51
61
  end
@@ -1,7 +1,6 @@
1
1
  # A much simpler source line cacher because linecache sucks at platform compat
2
2
 
3
3
  module Raven
4
-
5
4
  class LineCache
6
5
  class << self
7
6
  CACHE = {}
@@ -1,27 +1,11 @@
1
- require 'json'
2
-
3
1
  module Raven
4
2
  class Processor
5
- attr_accessor :sanitize_fields
6
-
7
3
  def initialize(client)
8
4
  @client = client
9
- @sanitize_fields = client.configuration.sanitize_fields
10
5
  end
11
6
 
12
7
  def process(data)
13
- data
14
- end
15
-
16
- private
17
-
18
- def parse_json_or_nil(string)
19
- begin
20
- OkJson.decode(string)
21
- rescue Raven::OkJson::Error
22
- nil
23
- end
8
+ raise NotImplementedError
24
9
  end
25
-
26
10
  end
27
11
  end
@@ -2,7 +2,11 @@ module Raven
2
2
  class Processor::RemoveStacktrace < Processor
3
3
 
4
4
  def process(value)
5
- value['exception'].delete('stacktrace') if value['exception']
5
+ if value[:exception]
6
+ value[:exception][:values].map do |single_exception|
7
+ single_exception.delete(:stacktrace) if single_exception[:stacktrace]
8
+ end
9
+ end
6
10
 
7
11
  value
8
12
  end
@@ -1,3 +1,4 @@
1
+ require 'json'
1
2
  module Raven
2
3
  class Processor::SanitizeData < Processor
3
4
  STRING_MASK = '********'
@@ -5,6 +6,13 @@ module Raven
5
6
  DEFAULT_FIELDS = %w(authorization password passwd secret ssn social(.*)?sec)
6
7
  CREDIT_CARD_RE = /^(?:\d[ -]*?){13,16}$/
7
8
 
9
+ attr_accessor :sanitize_fields
10
+
11
+ def initialize(client)
12
+ super
13
+ self.sanitize_fields = client.configuration.sanitize_fields
14
+ end
15
+
8
16
  def process(value)
9
17
  value.inject(value) { |memo,(k,v)| memo[k] = sanitize(k,v); memo }
10
18
  end
@@ -16,13 +24,17 @@ module Raven
16
24
  v.map{|a| sanitize(k, a)}
17
25
  elsif k == 'query_string'
18
26
  sanitize_query_string(v)
19
- elsif v.is_a?(String) && (json = parse_json_or_nil(v))
20
- #if this string is actually a json obj, convert and sanitize
21
- json.is_a?(Hash) ? process(json).to_json : v
22
- elsif v.is_a?(Integer) && (CREDIT_CARD_RE.match(v.to_s) || fields_re.match(k.to_s))
27
+ elsif v.is_a?(Integer) && matches_regexes?(k,v)
23
28
  INT_MASK
24
- elsif v.is_a?(String) && (CREDIT_CARD_RE.match(v.to_s) || fields_re.match(k.to_s))
25
- STRING_MASK
29
+ elsif v.is_a?(String)
30
+ if fields_re.match(v.to_s) && (json = parse_json_or_nil(v))
31
+ #if this string is actually a json obj, convert and sanitize
32
+ json.is_a?(Hash) ? process(json).to_json : v
33
+ elsif matches_regexes?(k,v)
34
+ STRING_MASK
35
+ else
36
+ v
37
+ end
26
38
  else
27
39
  v
28
40
  end
@@ -36,8 +48,20 @@ module Raven
36
48
  URI.encode_www_form(processed_query_hash)
37
49
  end
38
50
 
51
+ def matches_regexes?(k, v)
52
+ CREDIT_CARD_RE.match(v.to_s) || fields_re.match(k.to_s)
53
+ end
54
+
39
55
  def fields_re
40
- @fields_re ||= /(#{(DEFAULT_FIELDS + @sanitize_fields).join("|")})/i
56
+ @fields_re ||= /(#{(DEFAULT_FIELDS | sanitize_fields).join("|")})/i
57
+ end
58
+
59
+ def parse_json_or_nil(string)
60
+ begin
61
+ OkJson.decode(string)
62
+ rescue Raven::OkJson::Error, NoMethodError
63
+ nil
64
+ end
41
65
  end
42
66
  end
43
67
  end
data/lib/raven/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Raven
2
- VERSION = "0.12.3"
2
+ VERSION = "0.13.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sentry-raven
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.12.3
4
+ version: 0.13.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sentry Team
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-01-16 00:00:00.000000000 Z
11
+ date: 2015-04-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -66,20 +66,6 @@ dependencies:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: '1.16'
69
- - !ruby/object:Gem::Dependency
70
- name: coveralls
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - ">="
74
- - !ruby/object:Gem::Version
75
- version: '0'
76
- type: :development
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - ">="
81
- - !ruby/object:Gem::Version
82
- version: '0'
83
69
  - !ruby/object:Gem::Dependency
84
70
  name: rest-client
85
71
  requirement: !ruby/object:Gem::Requirement
@@ -124,7 +110,6 @@ files:
124
110
  - lib/raven/backports/uri.rb
125
111
  - lib/raven/backtrace.rb
126
112
  - lib/raven/base.rb
127
- - lib/raven/better_attr_accessor.rb
128
113
  - lib/raven/cli.rb
129
114
  - lib/raven/client.rb
130
115
  - lib/raven/configuration.rb
@@ -143,6 +128,7 @@ files:
143
128
  - lib/raven/interfaces/exception.rb
144
129
  - lib/raven/interfaces/http.rb
145
130
  - lib/raven/interfaces/message.rb
131
+ - lib/raven/interfaces/single_exception.rb
146
132
  - lib/raven/interfaces/stack_trace.rb
147
133
  - lib/raven/linecache.rb
148
134
  - lib/raven/logger.rb
@@ -1,44 +0,0 @@
1
- require 'set'
2
-
3
- module Raven
4
- module BetterAttrAccessor
5
-
6
- def attributes
7
- Hash[
8
- self.class.attributes.map do |attr|
9
- [attr, send(attr)]
10
- end
11
- ]
12
- end
13
-
14
- def self.included(base)
15
- base.extend ClassMethods
16
- end
17
-
18
- module ClassMethods
19
- def attributes
20
- @attributes ||= Set.new
21
-
22
- if superclass.include? BetterAttrAccessor
23
- @attributes + superclass.attributes
24
- else
25
- @attributes
26
- end
27
- end
28
-
29
- def attr_accessor(attr, options = {})
30
- @attributes ||= Set.new
31
- @attributes << attr.to_s
32
-
33
- define_method attr do
34
- if instance_variable_defined? "@#{attr}"
35
- instance_variable_get "@#{attr}"
36
- elsif options.key? :default
37
- instance_variable_set "@#{attr}", options[:default].dup
38
- end
39
- end
40
- attr_writer attr
41
- end
42
- end
43
- end
44
- end