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 +4 -4
- data/README.md +2 -3
- data/lib/raven/base.rb +43 -53
- data/lib/raven/client.rb +25 -29
- data/lib/raven/configuration.rb +7 -2
- data/lib/raven/event.rb +103 -84
- data/lib/raven/integrations/rack.rb +5 -12
- data/lib/raven/integrations/rails.rb +1 -1
- data/lib/raven/interfaces.rb +5 -6
- data/lib/raven/interfaces/exception.rb +3 -6
- data/lib/raven/interfaces/http.rb +1 -0
- data/lib/raven/interfaces/single_exception.rb +19 -0
- data/lib/raven/interfaces/stack_trace.rb +25 -15
- data/lib/raven/linecache.rb +0 -1
- data/lib/raven/processor.rb +1 -17
- data/lib/raven/processor/removestacktrace.rb +5 -1
- data/lib/raven/processor/sanitizedata.rb +31 -7
- data/lib/raven/version.rb +1 -1
- metadata +3 -17
- data/lib/raven/better_attr_accessor.rb +0 -44
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c09fc2d9d439ff50aa3d575716890797ebb81cb6
|
4
|
+
data.tar.gz: 834dc9c9d24c2bf004f071b9ec77d3c96edb22e7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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.
|
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
|
53
|
-
|
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
|
69
|
+
def configure
|
63
70
|
yield(configuration) if block_given?
|
64
71
|
|
65
72
|
self.client = Client.new(configuration)
|
66
|
-
|
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 = {}
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
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
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
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
|
121
|
-
|
122
|
-
|
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
|
27
|
-
configuration.log_excluded_environment_message
|
28
|
-
return
|
29
|
-
end
|
25
|
+
return false unless configuration_allows_sending
|
30
26
|
|
31
|
-
#
|
32
|
-
event
|
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
|
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
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
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
|
67
|
+
case configuration.encoding
|
68
68
|
when 'gzip'
|
69
|
-
|
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
|
-
|
71
|
+
['application/json', encoded]
|
74
72
|
end
|
75
73
|
end
|
76
74
|
|
77
75
|
def get_log_message(event)
|
78
|
-
(event && event
|
76
|
+
(event && event['message']) || '<no message value>'
|
79
77
|
end
|
80
78
|
|
81
79
|
def transport
|
82
80
|
@transport ||=
|
83
|
-
case
|
81
|
+
case configuration.scheme
|
84
82
|
when 'udp'
|
85
|
-
Transports::UDP.new
|
83
|
+
Transports::UDP.new(configuration)
|
86
84
|
when 'http', 'https'
|
87
|
-
Transports::HTTP.new
|
85
|
+
Transports::HTTP.new(configuration)
|
88
86
|
else
|
89
|
-
raise Error
|
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' =>
|
100
|
-
'sentry_secret' =>
|
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
|
data/lib/raven/configuration.rb
CHANGED
@@ -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
|
88
|
-
attr_accessor :
|
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
|
-
|
28
|
-
|
29
|
-
def initialize(
|
30
|
-
@configuration =
|
31
|
-
@interfaces
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
@
|
36
|
-
@
|
37
|
-
@
|
38
|
-
@
|
39
|
-
|
40
|
-
@
|
41
|
-
@
|
42
|
-
@
|
43
|
-
@
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
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
|
-
@
|
53
|
-
@extra.merge!(context.extra)
|
55
|
+
init.each_pair { |key, val| instance_variable_set('@' + key.to_s, val) }
|
54
56
|
|
55
|
-
@
|
57
|
+
@user.merge!(@context.user)
|
58
|
+
@extra.merge!(@context.extra)
|
56
59
|
@tags.merge!(@configuration.tags)
|
57
|
-
@tags.merge!(
|
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
|
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
|
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
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
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[
|
112
|
-
data[
|
113
|
-
data[
|
114
|
-
data[
|
115
|
-
data[
|
116
|
-
data[
|
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
|
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.
|
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.
|
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
|
-
|
37
|
-
|
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)
|
data/lib/raven/interfaces.rb
CHANGED
@@ -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
|
-
|
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 :
|
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[
|
15
|
-
data[
|
11
|
+
if data[:values]
|
12
|
+
data[:values] = data[:values].map(&:to_hash)
|
16
13
|
end
|
17
14
|
data
|
18
15
|
end
|
@@ -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
|
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[
|
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
|
29
|
-
attr_accessor :pre_context
|
30
|
-
attr_accessor :post_context
|
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 =
|
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[
|
45
|
-
data.delete(
|
46
|
-
data.delete(
|
47
|
-
data.delete(
|
48
|
-
data.delete(
|
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
|
data/lib/raven/linecache.rb
CHANGED
data/lib/raven/processor.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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?(
|
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)
|
25
|
-
|
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
|
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
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.
|
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-
|
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
|