alec-gem 2.7.2
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.
- checksums.yaml +7 -0
- data/.rspec +1 -0
- data/.rubocop.yml +74 -0
- data/.travis.yml +47 -0
- data/Gemfile +38 -0
- data/Gemfile.lock +215 -0
- data/LICENSE +201 -0
- data/README.md +132 -0
- data/Rakefile +29 -0
- data/alec-gem.gemspec +22 -0
- data/changelog.md +442 -0
- data/docs/Makefile +130 -0
- data/docs/breadcrumbs.rst +51 -0
- data/docs/conf.py +228 -0
- data/docs/config.rst +260 -0
- data/docs/context.rst +141 -0
- data/docs/index.rst +113 -0
- data/docs/install.rst +40 -0
- data/docs/integrations/heroku.rst +11 -0
- data/docs/integrations/index.rst +59 -0
- data/docs/integrations/puma.rst +30 -0
- data/docs/integrations/rack.rst +27 -0
- data/docs/integrations/rails.rst +62 -0
- data/docs/make.bat +155 -0
- data/docs/processors.rst +124 -0
- data/docs/sentry-doc-config.json +31 -0
- data/docs/usage.rst +176 -0
- data/exe/raven +32 -0
- data/lib/raven.rb +3 -0
- data/lib/raven/backtrace.rb +137 -0
- data/lib/raven/base.rb +106 -0
- data/lib/raven/breadcrumbs.rb +76 -0
- data/lib/raven/breadcrumbs/activesupport.rb +19 -0
- data/lib/raven/breadcrumbs/logger.rb +93 -0
- data/lib/raven/cli.rb +59 -0
- data/lib/raven/client.rb +142 -0
- data/lib/raven/configuration.rb +434 -0
- data/lib/raven/context.rb +43 -0
- data/lib/raven/event.rb +259 -0
- data/lib/raven/instance.rb +221 -0
- data/lib/raven/integrations/delayed_job.rb +58 -0
- data/lib/raven/integrations/rack-timeout.rb +19 -0
- data/lib/raven/integrations/rack.rb +139 -0
- data/lib/raven/integrations/rails.rb +79 -0
- data/lib/raven/integrations/rails/active_job.rb +59 -0
- data/lib/raven/integrations/rails/controller_methods.rb +13 -0
- data/lib/raven/integrations/rails/controller_transaction.rb +13 -0
- data/lib/raven/integrations/rails/overrides/debug_exceptions_catcher.rb +31 -0
- data/lib/raven/integrations/rails/overrides/streaming_reporter.rb +23 -0
- data/lib/raven/integrations/railties.rb +1 -0
- data/lib/raven/integrations/rake.rb +18 -0
- data/lib/raven/integrations/sidekiq.rb +87 -0
- data/lib/raven/integrations/tasks.rb +11 -0
- data/lib/raven/interface.rb +25 -0
- data/lib/raven/interfaces/exception.rb +15 -0
- data/lib/raven/interfaces/http.rb +16 -0
- data/lib/raven/interfaces/message.rb +20 -0
- data/lib/raven/interfaces/single_exception.rb +14 -0
- data/lib/raven/interfaces/stack_trace.rb +69 -0
- data/lib/raven/linecache.rb +41 -0
- data/lib/raven/logger.rb +19 -0
- data/lib/raven/processor.rb +15 -0
- data/lib/raven/processor/cookies.rb +26 -0
- data/lib/raven/processor/http_headers.rb +55 -0
- data/lib/raven/processor/post_data.rb +22 -0
- data/lib/raven/processor/removecircularreferences.rb +17 -0
- data/lib/raven/processor/removestacktrace.rb +24 -0
- data/lib/raven/processor/sanitizedata.rb +88 -0
- data/lib/raven/processor/utf8conversion.rb +52 -0
- data/lib/raven/transports.rb +15 -0
- data/lib/raven/transports/dummy.rb +16 -0
- data/lib/raven/transports/http.rb +68 -0
- data/lib/raven/utils/deep_merge.rb +22 -0
- data/lib/raven/utils/real_ip.rb +62 -0
- data/lib/raven/version.rb +5 -0
- data/lib/sentry-raven-without-integrations.rb +1 -0
- data/lib/sentry-raven.rb +1 -0
- data/pkg/sentry-raven-2.7.2.gem +0 -0
- metadata +143 -0
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'rbconfig'
|
2
|
+
|
3
|
+
module Raven
|
4
|
+
class Context
|
5
|
+
def self.current
|
6
|
+
Thread.current[:sentry_context] ||= new
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.clear!
|
10
|
+
Thread.current[:sentry_context] = nil
|
11
|
+
end
|
12
|
+
|
13
|
+
attr_accessor :transaction, :extra, :server_os, :rack_env, :runtime, :tags, :user
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
self.server_os = self.class.os_context
|
17
|
+
self.runtime = self.class.runtime_context
|
18
|
+
self.extra = { :server => { :os => server_os, :runtime => runtime } }
|
19
|
+
self.rack_env = nil
|
20
|
+
self.tags = {}
|
21
|
+
self.user = {}
|
22
|
+
self.transaction = []
|
23
|
+
end
|
24
|
+
|
25
|
+
class << self
|
26
|
+
def os_context
|
27
|
+
@os_context ||= {
|
28
|
+
:name => Raven.sys_command("uname -s") || RbConfig::CONFIG["host_os"],
|
29
|
+
:version => Raven.sys_command("uname -v"),
|
30
|
+
:build => Raven.sys_command("uname -r"),
|
31
|
+
:kernel_version => Raven.sys_command("uname -a") || Raven.sys_command("ver") # windows
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
def runtime_context
|
36
|
+
@runtime_context ||= {
|
37
|
+
:name => RbConfig::CONFIG["ruby_install_name"],
|
38
|
+
:version => Raven.sys_command("ruby -v")
|
39
|
+
}
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/lib/raven/event.rb
ADDED
@@ -0,0 +1,259 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'socket'
|
3
|
+
require 'securerandom'
|
4
|
+
|
5
|
+
module Raven
|
6
|
+
class Event
|
7
|
+
# See Sentry server default limits at
|
8
|
+
# https://github.com/getsentry/sentry/blob/master/src/sentry/conf/server.py
|
9
|
+
MAX_MESSAGE_SIZE_IN_BYTES = 1024 * 8
|
10
|
+
|
11
|
+
SDK = { "name" => "raven-ruby", "version" => Raven::VERSION }.freeze
|
12
|
+
|
13
|
+
attr_accessor :id, :logger, :transaction, :server_name, :release, :modules,
|
14
|
+
:extra, :tags, :context, :configuration, :checksum,
|
15
|
+
:fingerprint, :environment, :server_os, :runtime,
|
16
|
+
:breadcrumbs, :user, :backtrace, :platform, :sdk
|
17
|
+
alias event_id id
|
18
|
+
|
19
|
+
attr_reader :level, :timestamp, :time_spent
|
20
|
+
|
21
|
+
def initialize(init = {})
|
22
|
+
# Set some simple default values
|
23
|
+
self.id = SecureRandom.uuid.delete("-")
|
24
|
+
self.timestamp = Time.now.utc
|
25
|
+
self.level = :error
|
26
|
+
self.logger = :ruby
|
27
|
+
self.platform = :ruby
|
28
|
+
self.sdk = SDK
|
29
|
+
|
30
|
+
# Set some attributes with empty hashes to allow merging
|
31
|
+
@interfaces = {}
|
32
|
+
self.user = {} # TODO: contexts
|
33
|
+
self.extra = {} # TODO: contexts
|
34
|
+
self.server_os = {} # TODO: contexts
|
35
|
+
self.runtime = {} # TODO: contexts
|
36
|
+
self.tags = {} # TODO: contexts
|
37
|
+
|
38
|
+
copy_initial_state
|
39
|
+
|
40
|
+
# Allow attributes to be set on the event at initialization
|
41
|
+
yield self if block_given?
|
42
|
+
init.each_pair { |key, val| public_send("#{key}=", val) }
|
43
|
+
|
44
|
+
set_core_attributes_from_configuration
|
45
|
+
set_core_attributes_from_context
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.from_exception(exc, options = {}, &block)
|
49
|
+
exception_context = if exc.instance_variable_defined?(:@__raven_context)
|
50
|
+
exc.instance_variable_get(:@__raven_context)
|
51
|
+
elsif exc.respond_to?(:raven_context)
|
52
|
+
exc.raven_context
|
53
|
+
else
|
54
|
+
{}
|
55
|
+
end
|
56
|
+
options = Raven::Utils::DeepMergeHash.deep_merge(exception_context, options)
|
57
|
+
|
58
|
+
configuration = options[:configuration] || Raven.configuration
|
59
|
+
return unless configuration.exception_class_allowed?(exc)
|
60
|
+
|
61
|
+
new(options) do |evt|
|
62
|
+
evt.message = "#{exc.class}: #{exc.message}"
|
63
|
+
|
64
|
+
evt.add_exception_interface(exc)
|
65
|
+
|
66
|
+
yield evt if block
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.from_message(message, options = {})
|
71
|
+
new(options) do |evt|
|
72
|
+
evt.message = message, options[:message_params] || []
|
73
|
+
if options[:backtrace]
|
74
|
+
evt.interface(:stacktrace) do |int|
|
75
|
+
int.frames = evt.stacktrace_interface_from(options[:backtrace])
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def message
|
82
|
+
@interfaces[:logentry] && @interfaces[:logentry].unformatted_message
|
83
|
+
end
|
84
|
+
|
85
|
+
def message=(args)
|
86
|
+
message, params = *args
|
87
|
+
interface(:message) do |int|
|
88
|
+
int.message = message.byteslice(0...MAX_MESSAGE_SIZE_IN_BYTES) # Messages limited to 10kb
|
89
|
+
int.params = params
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def timestamp=(time)
|
94
|
+
@timestamp = time.is_a?(Time) ? time.strftime('%Y-%m-%dT%H:%M:%S') : time
|
95
|
+
end
|
96
|
+
|
97
|
+
def time_spent=(time)
|
98
|
+
@time_spent = time.is_a?(Float) ? (time * 1000).to_i : time
|
99
|
+
end
|
100
|
+
|
101
|
+
def level=(new_level) # needed to meet the Sentry spec
|
102
|
+
@level = new_level == "warn" || new_level == :warn ? :warning : new_level
|
103
|
+
end
|
104
|
+
|
105
|
+
def interface(name, value = nil, &block)
|
106
|
+
int = Interface.registered[name]
|
107
|
+
raise(Error, "Unknown interface: #{name}") unless int
|
108
|
+
@interfaces[int.sentry_alias] = int.new(value, &block) if value || block
|
109
|
+
@interfaces[int.sentry_alias]
|
110
|
+
end
|
111
|
+
|
112
|
+
def [](key)
|
113
|
+
interface(key)
|
114
|
+
end
|
115
|
+
|
116
|
+
def []=(key, value)
|
117
|
+
interface(key, value)
|
118
|
+
end
|
119
|
+
|
120
|
+
def to_hash
|
121
|
+
data = [:checksum, :environment, :event_id, :extra, :fingerprint, :level,
|
122
|
+
:logger, :message, :modules, :platform, :release, :sdk, :server_name,
|
123
|
+
:tags, :time_spent, :timestamp, :transaction, :user].each_with_object({}) do |att, memo|
|
124
|
+
memo[att] = public_send(att) if public_send(att)
|
125
|
+
end
|
126
|
+
|
127
|
+
data[:breadcrumbs] = @breadcrumbs.to_hash unless @breadcrumbs.empty?
|
128
|
+
|
129
|
+
@interfaces.each_pair do |name, int_data|
|
130
|
+
data[name.to_sym] = int_data.to_hash
|
131
|
+
end
|
132
|
+
data
|
133
|
+
end
|
134
|
+
|
135
|
+
def to_json_compatible
|
136
|
+
cleaned_hash = async_json_processors.reduce(to_hash) { |a, e| e.process(a) }
|
137
|
+
JSON.parse(JSON.generate(cleaned_hash))
|
138
|
+
end
|
139
|
+
|
140
|
+
def add_exception_interface(exc)
|
141
|
+
interface(:exception) do |exc_int|
|
142
|
+
exceptions = exception_chain_to_array(exc)
|
143
|
+
backtraces = Set.new
|
144
|
+
exc_int.values = exceptions.map do |e|
|
145
|
+
SingleExceptionInterface.new do |int|
|
146
|
+
int.type = e.class.to_s
|
147
|
+
int.value = e.to_s
|
148
|
+
int.module = e.class.to_s.split('::')[0...-1].join('::')
|
149
|
+
|
150
|
+
int.stacktrace =
|
151
|
+
if e.backtrace && !backtraces.include?(e.backtrace.object_id)
|
152
|
+
backtraces << e.backtrace.object_id
|
153
|
+
StacktraceInterface.new do |stacktrace|
|
154
|
+
stacktrace.frames = stacktrace_interface_from(e.backtrace)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def stacktrace_interface_from(backtrace)
|
163
|
+
Backtrace.parse(backtrace).lines.reverse.each_with_object([]) do |line, memo|
|
164
|
+
frame = StacktraceInterface::Frame.new
|
165
|
+
frame.abs_path = line.file if line.file
|
166
|
+
frame.function = line.method if line.method
|
167
|
+
frame.lineno = line.number
|
168
|
+
frame.in_app = line.in_app
|
169
|
+
frame.module = line.module_name if line.module_name
|
170
|
+
|
171
|
+
if configuration[:context_lines] && frame.abs_path
|
172
|
+
frame.pre_context, frame.context_line, frame.post_context = \
|
173
|
+
configuration.linecache.get_file_context(frame.abs_path, frame.lineno, configuration[:context_lines])
|
174
|
+
end
|
175
|
+
|
176
|
+
memo << frame if frame.filename
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
# For cross-language compat
|
181
|
+
class << self
|
182
|
+
alias captureException from_exception
|
183
|
+
alias captureMessage from_message
|
184
|
+
alias capture_exception from_exception
|
185
|
+
alias capture_message from_message
|
186
|
+
end
|
187
|
+
|
188
|
+
private
|
189
|
+
|
190
|
+
def copy_initial_state
|
191
|
+
self.configuration = Raven.configuration
|
192
|
+
self.breadcrumbs = Raven.breadcrumbs
|
193
|
+
self.context = Raven.context
|
194
|
+
end
|
195
|
+
|
196
|
+
def set_core_attributes_from_configuration
|
197
|
+
self.server_name ||= configuration.server_name
|
198
|
+
self.release ||= configuration.release
|
199
|
+
self.modules = list_gem_specs if configuration.send_modules
|
200
|
+
self.environment ||= configuration.current_environment
|
201
|
+
end
|
202
|
+
|
203
|
+
def set_core_attributes_from_context
|
204
|
+
self.transaction ||= context.transaction.last
|
205
|
+
|
206
|
+
# If this is a Rack event, merge Rack context
|
207
|
+
add_rack_context if !self[:http] && context.rack_env
|
208
|
+
|
209
|
+
# Merge contexts
|
210
|
+
self.user = context.user.merge(user) # TODO: contexts
|
211
|
+
self.extra = context.extra.merge(extra) # TODO: contexts
|
212
|
+
self.tags = configuration.tags.merge(context.tags).merge!(tags) # TODO: contexts
|
213
|
+
end
|
214
|
+
|
215
|
+
def add_rack_context
|
216
|
+
interface :http do |int|
|
217
|
+
int.from_rack(context.rack_env)
|
218
|
+
end
|
219
|
+
context.user[:ip_address] = calculate_real_ip_from_rack
|
220
|
+
end
|
221
|
+
|
222
|
+
# When behind a proxy (or if the user is using a proxy), we can't use
|
223
|
+
# REMOTE_ADDR to determine the Event IP, and must use other headers instead.
|
224
|
+
def calculate_real_ip_from_rack
|
225
|
+
Utils::RealIp.new(
|
226
|
+
:remote_addr => context.rack_env["REMOTE_ADDR"],
|
227
|
+
:client_ip => context.rack_env["HTTP_CLIENT_IP"],
|
228
|
+
:real_ip => context.rack_env["HTTP_X_REAL_IP"],
|
229
|
+
:forwarded_for => context.rack_env["HTTP_X_FORWARDED_FOR"]
|
230
|
+
).calculate_ip
|
231
|
+
end
|
232
|
+
|
233
|
+
def async_json_processors
|
234
|
+
[
|
235
|
+
Raven::Processor::RemoveCircularReferences,
|
236
|
+
Raven::Processor::UTF8Conversion
|
237
|
+
].map { |v| v.new(self) }
|
238
|
+
end
|
239
|
+
|
240
|
+
def exception_chain_to_array(exc)
|
241
|
+
if exc.respond_to?(:cause) && exc.cause
|
242
|
+
exceptions = [exc]
|
243
|
+
while exc.cause
|
244
|
+
exc = exc.cause
|
245
|
+
break if exceptions.any? { |e| e.object_id == exc.object_id }
|
246
|
+
exceptions << exc
|
247
|
+
end
|
248
|
+
exceptions.reverse!
|
249
|
+
else
|
250
|
+
[exc]
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
def list_gem_specs
|
255
|
+
# Older versions of Rubygems don't support iterating over all specs
|
256
|
+
Hash[Gem::Specification.map { |spec| [spec.name, spec.version.to_s] }] if Gem::Specification.respond_to?(:map)
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end
|
@@ -0,0 +1,221 @@
|
|
1
|
+
module Raven
|
2
|
+
# A copy of Raven's base module class methods, minus some of the integration
|
3
|
+
# and global hooks since it's meant to be used explicitly. Useful for
|
4
|
+
# sending errors to multiple sentry projects in a large application.
|
5
|
+
#
|
6
|
+
# @example
|
7
|
+
# class Foo
|
8
|
+
# def initialize
|
9
|
+
# @other_raven = Raven::Instance.new
|
10
|
+
# @other_raven.configure do |config|
|
11
|
+
# config.server = 'http://...'
|
12
|
+
# end
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# def foo
|
16
|
+
# # ...
|
17
|
+
# rescue => e
|
18
|
+
# @other_raven.capture_exception(e)
|
19
|
+
# end
|
20
|
+
# end
|
21
|
+
class Instance
|
22
|
+
# See Raven::Client.
|
23
|
+
attr_writer :client
|
24
|
+
|
25
|
+
# See Raven::Configuration.
|
26
|
+
attr_accessor :configuration
|
27
|
+
|
28
|
+
def initialize(context = nil, config = nil)
|
29
|
+
@context = @explicit_context = context
|
30
|
+
self.configuration = config || Configuration.new
|
31
|
+
end
|
32
|
+
|
33
|
+
def context
|
34
|
+
if @explicit_context
|
35
|
+
@context ||= Context.new
|
36
|
+
else
|
37
|
+
Context.current
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def logger
|
42
|
+
configuration.logger
|
43
|
+
end
|
44
|
+
|
45
|
+
# The client object is responsible for delivering formatted data to the
|
46
|
+
# Sentry server.
|
47
|
+
def client
|
48
|
+
@client ||= Client.new(configuration)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Tell the log that the client is good to go
|
52
|
+
def report_status
|
53
|
+
return if configuration.silence_ready
|
54
|
+
if configuration.capture_allowed?
|
55
|
+
logger.info "Raven #{VERSION} ready to catch errors"
|
56
|
+
else
|
57
|
+
logger.info "Raven #{VERSION} configured not to capture errors: #{configuration.error_messages}"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Call this method to modify defaults in your initializers.
|
62
|
+
#
|
63
|
+
# @example
|
64
|
+
# Raven.configure do |config|
|
65
|
+
# config.server = 'http://...'
|
66
|
+
# end
|
67
|
+
def configure
|
68
|
+
yield(configuration) if block_given?
|
69
|
+
|
70
|
+
self.client = Client.new(configuration)
|
71
|
+
report_status
|
72
|
+
client
|
73
|
+
end
|
74
|
+
|
75
|
+
# Send an event to the configured Sentry server
|
76
|
+
#
|
77
|
+
# @example
|
78
|
+
# evt = Raven::Event.new(:message => "An error")
|
79
|
+
# Raven.send_event(evt)
|
80
|
+
def send_event(event)
|
81
|
+
client.send_event(event)
|
82
|
+
end
|
83
|
+
|
84
|
+
# Capture and process any exceptions from the given block.
|
85
|
+
#
|
86
|
+
# @example
|
87
|
+
# Raven.capture do
|
88
|
+
# MyApp.run
|
89
|
+
# end
|
90
|
+
def capture(options = {})
|
91
|
+
if block_given?
|
92
|
+
begin
|
93
|
+
yield
|
94
|
+
rescue Error
|
95
|
+
raise # Don't capture Raven errors
|
96
|
+
rescue Exception => e
|
97
|
+
capture_type(e, options)
|
98
|
+
raise
|
99
|
+
end
|
100
|
+
else
|
101
|
+
install_at_exit_hook(options)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def capture_type(obj, options = {})
|
106
|
+
unless configuration.capture_allowed?(obj)
|
107
|
+
logger.debug("#{obj} excluded from capture: #{configuration.error_messages}")
|
108
|
+
return false
|
109
|
+
end
|
110
|
+
|
111
|
+
message_or_exc = obj.is_a?(String) ? "message" : "exception"
|
112
|
+
options[:configuration] = configuration
|
113
|
+
options[:context] = context
|
114
|
+
if (evt = Event.send("from_" + message_or_exc, obj, options))
|
115
|
+
yield evt if block_given?
|
116
|
+
if configuration.async?
|
117
|
+
begin
|
118
|
+
# We have to convert to a JSON-like hash, because background job
|
119
|
+
# processors (esp ActiveJob) may not like weird types in the event hash
|
120
|
+
configuration.async.call(evt.to_json_compatible)
|
121
|
+
rescue => ex
|
122
|
+
logger.error("async event sending failed: #{ex.message}")
|
123
|
+
send_event(evt)
|
124
|
+
end
|
125
|
+
else
|
126
|
+
send_event(evt)
|
127
|
+
end
|
128
|
+
Thread.current["sentry_#{object_id}_last_event_id".to_sym] = evt.id
|
129
|
+
evt
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
alias capture_message capture_type
|
134
|
+
alias capture_exception capture_type
|
135
|
+
|
136
|
+
def last_event_id
|
137
|
+
Thread.current["sentry_#{object_id}_last_event_id".to_sym]
|
138
|
+
end
|
139
|
+
|
140
|
+
# Provides extra context to the exception prior to it being handled by
|
141
|
+
# Raven. An exception can have multiple annotations, which are merged
|
142
|
+
# together.
|
143
|
+
#
|
144
|
+
# The options (annotation) is treated the same as the ``options``
|
145
|
+
# parameter to ``capture_exception`` or ``Event.from_exception``, and
|
146
|
+
# can contain the same ``:user``, ``:tags``, etc. options as these
|
147
|
+
# methods.
|
148
|
+
#
|
149
|
+
# These will be merged with the ``options`` parameter to
|
150
|
+
# ``Event.from_exception`` at the top of execution.
|
151
|
+
#
|
152
|
+
# @example
|
153
|
+
# begin
|
154
|
+
# raise "Hello"
|
155
|
+
# rescue => exc
|
156
|
+
# Raven.annotate_exception(exc, :user => { 'id' => 1,
|
157
|
+
# 'email' => 'foo@example.com' })
|
158
|
+
# end
|
159
|
+
def annotate_exception(exc, options = {})
|
160
|
+
notes = (exc.instance_variable_defined?(:@__raven_context) && exc.instance_variable_get(:@__raven_context)) || {}
|
161
|
+
Raven::Utils::DeepMergeHash.deep_merge!(notes, options)
|
162
|
+
exc.instance_variable_set(:@__raven_context, notes)
|
163
|
+
exc
|
164
|
+
end
|
165
|
+
|
166
|
+
# Bind user context. Merges with existing context (if any).
|
167
|
+
#
|
168
|
+
# It is recommending that you send at least the ``id`` and ``email``
|
169
|
+
# values. All other values are arbitrary.
|
170
|
+
#
|
171
|
+
# @example
|
172
|
+
# Raven.user_context('id' => 1, 'email' => 'foo@example.com')
|
173
|
+
def user_context(options = nil)
|
174
|
+
context.user = options || {}
|
175
|
+
end
|
176
|
+
|
177
|
+
# Bind tags context. Merges with existing context (if any).
|
178
|
+
#
|
179
|
+
# Tags are key / value pairs which generally represent things like
|
180
|
+
# application version, environment, role, and server names.
|
181
|
+
#
|
182
|
+
# @example
|
183
|
+
# Raven.tags_context('my_custom_tag' => 'tag_value')
|
184
|
+
def tags_context(options = nil)
|
185
|
+
context.tags.merge!(options || {})
|
186
|
+
end
|
187
|
+
|
188
|
+
# Bind extra context. Merges with existing context (if any).
|
189
|
+
#
|
190
|
+
# Extra context shows up as Additional Data within Sentry, and is
|
191
|
+
# completely arbitrary.
|
192
|
+
#
|
193
|
+
# @example
|
194
|
+
# Raven.extra_context('my_custom_data' => 'value')
|
195
|
+
def extra_context(options = nil)
|
196
|
+
context.extra.merge!(options || {})
|
197
|
+
end
|
198
|
+
|
199
|
+
def rack_context(env)
|
200
|
+
env = nil if env.empty?
|
201
|
+
|
202
|
+
context.rack_env = env
|
203
|
+
end
|
204
|
+
|
205
|
+
def breadcrumbs
|
206
|
+
BreadcrumbBuffer.current
|
207
|
+
end
|
208
|
+
|
209
|
+
private
|
210
|
+
|
211
|
+
def install_at_exit_hook(options)
|
212
|
+
at_exit do
|
213
|
+
exception = $ERROR_INFO
|
214
|
+
if exception
|
215
|
+
logger.debug "Caught a post-mortem exception: #{exception.inspect}"
|
216
|
+
capture_type(exception, options)
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|