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.
Files changed (79) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +1 -0
  3. data/.rubocop.yml +74 -0
  4. data/.travis.yml +47 -0
  5. data/Gemfile +38 -0
  6. data/Gemfile.lock +215 -0
  7. data/LICENSE +201 -0
  8. data/README.md +132 -0
  9. data/Rakefile +29 -0
  10. data/alec-gem.gemspec +22 -0
  11. data/changelog.md +442 -0
  12. data/docs/Makefile +130 -0
  13. data/docs/breadcrumbs.rst +51 -0
  14. data/docs/conf.py +228 -0
  15. data/docs/config.rst +260 -0
  16. data/docs/context.rst +141 -0
  17. data/docs/index.rst +113 -0
  18. data/docs/install.rst +40 -0
  19. data/docs/integrations/heroku.rst +11 -0
  20. data/docs/integrations/index.rst +59 -0
  21. data/docs/integrations/puma.rst +30 -0
  22. data/docs/integrations/rack.rst +27 -0
  23. data/docs/integrations/rails.rst +62 -0
  24. data/docs/make.bat +155 -0
  25. data/docs/processors.rst +124 -0
  26. data/docs/sentry-doc-config.json +31 -0
  27. data/docs/usage.rst +176 -0
  28. data/exe/raven +32 -0
  29. data/lib/raven.rb +3 -0
  30. data/lib/raven/backtrace.rb +137 -0
  31. data/lib/raven/base.rb +106 -0
  32. data/lib/raven/breadcrumbs.rb +76 -0
  33. data/lib/raven/breadcrumbs/activesupport.rb +19 -0
  34. data/lib/raven/breadcrumbs/logger.rb +93 -0
  35. data/lib/raven/cli.rb +59 -0
  36. data/lib/raven/client.rb +142 -0
  37. data/lib/raven/configuration.rb +434 -0
  38. data/lib/raven/context.rb +43 -0
  39. data/lib/raven/event.rb +259 -0
  40. data/lib/raven/instance.rb +221 -0
  41. data/lib/raven/integrations/delayed_job.rb +58 -0
  42. data/lib/raven/integrations/rack-timeout.rb +19 -0
  43. data/lib/raven/integrations/rack.rb +139 -0
  44. data/lib/raven/integrations/rails.rb +79 -0
  45. data/lib/raven/integrations/rails/active_job.rb +59 -0
  46. data/lib/raven/integrations/rails/controller_methods.rb +13 -0
  47. data/lib/raven/integrations/rails/controller_transaction.rb +13 -0
  48. data/lib/raven/integrations/rails/overrides/debug_exceptions_catcher.rb +31 -0
  49. data/lib/raven/integrations/rails/overrides/streaming_reporter.rb +23 -0
  50. data/lib/raven/integrations/railties.rb +1 -0
  51. data/lib/raven/integrations/rake.rb +18 -0
  52. data/lib/raven/integrations/sidekiq.rb +87 -0
  53. data/lib/raven/integrations/tasks.rb +11 -0
  54. data/lib/raven/interface.rb +25 -0
  55. data/lib/raven/interfaces/exception.rb +15 -0
  56. data/lib/raven/interfaces/http.rb +16 -0
  57. data/lib/raven/interfaces/message.rb +20 -0
  58. data/lib/raven/interfaces/single_exception.rb +14 -0
  59. data/lib/raven/interfaces/stack_trace.rb +69 -0
  60. data/lib/raven/linecache.rb +41 -0
  61. data/lib/raven/logger.rb +19 -0
  62. data/lib/raven/processor.rb +15 -0
  63. data/lib/raven/processor/cookies.rb +26 -0
  64. data/lib/raven/processor/http_headers.rb +55 -0
  65. data/lib/raven/processor/post_data.rb +22 -0
  66. data/lib/raven/processor/removecircularreferences.rb +17 -0
  67. data/lib/raven/processor/removestacktrace.rb +24 -0
  68. data/lib/raven/processor/sanitizedata.rb +88 -0
  69. data/lib/raven/processor/utf8conversion.rb +52 -0
  70. data/lib/raven/transports.rb +15 -0
  71. data/lib/raven/transports/dummy.rb +16 -0
  72. data/lib/raven/transports/http.rb +68 -0
  73. data/lib/raven/utils/deep_merge.rb +22 -0
  74. data/lib/raven/utils/real_ip.rb +62 -0
  75. data/lib/raven/version.rb +5 -0
  76. data/lib/sentry-raven-without-integrations.rb +1 -0
  77. data/lib/sentry-raven.rb +1 -0
  78. data/pkg/sentry-raven-2.7.2.gem +0 -0
  79. 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
@@ -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