sentry-raven 2.1.3 → 3.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +5 -5
  2. data/.craft.yml +19 -0
  3. data/.scripts/bump-version.rb +5 -0
  4. data/CHANGELOG.md +703 -0
  5. data/Gemfile +37 -0
  6. data/Makefile +3 -0
  7. data/README.md +116 -18
  8. data/Rakefile +30 -0
  9. data/exe/raven +32 -0
  10. data/lib/raven/backtrace.rb +16 -6
  11. data/lib/raven/base.rb +17 -4
  12. data/lib/raven/breadcrumbs/{activesupport.rb → active_support_logger.rb} +9 -3
  13. data/lib/raven/breadcrumbs/logger.rb +2 -92
  14. data/lib/raven/breadcrumbs/sentry_logger.rb +73 -0
  15. data/lib/raven/breadcrumbs.rb +3 -1
  16. data/lib/raven/cli.rb +31 -43
  17. data/lib/raven/client.rb +39 -17
  18. data/lib/raven/configuration.rb +277 -37
  19. data/lib/raven/context.rb +17 -11
  20. data/lib/raven/core_ext/object/deep_dup.rb +57 -0
  21. data/lib/raven/core_ext/object/duplicable.rb +153 -0
  22. data/lib/raven/event.rb +172 -233
  23. data/lib/raven/helpers/deprecation_helper.rb +17 -0
  24. data/lib/raven/instance.rb +51 -25
  25. data/lib/raven/integrations/delayed_job.rb +18 -18
  26. data/lib/raven/integrations/rack-timeout.rb +11 -5
  27. data/lib/raven/integrations/rack.rb +36 -19
  28. data/lib/raven/integrations/rails/active_job.rb +52 -20
  29. data/lib/raven/integrations/rails/backtrace_cleaner.rb +29 -0
  30. data/lib/raven/integrations/rails/controller_transaction.rb +13 -0
  31. data/lib/raven/integrations/rails/overrides/debug_exceptions_catcher.rb +2 -2
  32. data/lib/raven/integrations/rails.rb +24 -8
  33. data/lib/raven/integrations/rake.rb +6 -1
  34. data/lib/raven/integrations/sidekiq/cleanup_middleware.rb +13 -0
  35. data/lib/raven/integrations/sidekiq/error_handler.rb +38 -0
  36. data/lib/raven/integrations/sidekiq.rb +6 -57
  37. data/lib/raven/interface.rb +2 -2
  38. data/lib/raven/interfaces/exception.rb +0 -2
  39. data/lib/raven/interfaces/http.rb +0 -2
  40. data/lib/raven/interfaces/message.rb +1 -1
  41. data/lib/raven/interfaces/single_exception.rb +0 -2
  42. data/lib/raven/interfaces/stack_trace.rb +19 -27
  43. data/lib/raven/linecache.rb +34 -17
  44. data/lib/raven/logger.rb +11 -18
  45. data/lib/raven/processor/cookies.rb +27 -7
  46. data/lib/raven/processor/http_headers.rb +18 -5
  47. data/lib/raven/processor/post_data.rb +16 -3
  48. data/lib/raven/processor/removecircularreferences.rb +12 -8
  49. data/lib/raven/processor/removestacktrace.rb +17 -6
  50. data/lib/raven/processor/sanitizedata.rb +88 -29
  51. data/lib/raven/processor/utf8conversion.rb +39 -14
  52. data/lib/raven/processor.rb +1 -1
  53. data/lib/raven/transports/http.rb +29 -21
  54. data/lib/raven/transports/stdout.rb +20 -0
  55. data/lib/raven/transports.rb +4 -8
  56. data/lib/raven/utils/context_filter.rb +42 -0
  57. data/lib/raven/utils/deep_merge.rb +6 -12
  58. data/lib/raven/utils/exception_cause_chain.rb +20 -0
  59. data/lib/raven/utils/real_ip.rb +1 -1
  60. data/lib/raven/utils/request_id.rb +16 -0
  61. data/lib/raven/version.rb +2 -2
  62. data/lib/sentry-raven-without-integrations.rb +6 -1
  63. data/lib/sentry_raven_without_integrations.rb +1 -0
  64. data/sentry-raven.gemspec +28 -0
  65. metadata +37 -103
  66. data/lib/raven/error.rb +0 -4
@@ -0,0 +1,153 @@
1
+ # frozen_string_literal: true
2
+
3
+ #########################################
4
+ # This file was copied from Rails 5.2 #
5
+ #########################################
6
+
7
+ #--
8
+ # Most objects are cloneable, but not all. For example you can't dup methods:
9
+ #
10
+ # method(:puts).dup # => TypeError: allocator undefined for Method
11
+ #
12
+ # Classes may signal their instances are not duplicable removing +dup+/+clone+
13
+ # or raising exceptions from them. So, to dup an arbitrary object you normally
14
+ # use an optimistic approach and are ready to catch an exception, say:
15
+ #
16
+ # arbitrary_object.dup rescue object
17
+ #
18
+ # Rails dups objects in a few critical spots where they are not that arbitrary.
19
+ # That rescue is very expensive (like 40 times slower than a predicate), and it
20
+ # is often triggered.
21
+ #
22
+ # That's why we hardcode the following cases and check duplicable? instead of
23
+ # using that rescue idiom.
24
+ #++
25
+ class Object
26
+ # Can you safely dup this object?
27
+ #
28
+ # False for method objects;
29
+ # true otherwise.
30
+ def duplicable?
31
+ true
32
+ end
33
+ end
34
+
35
+ class NilClass
36
+ begin
37
+ nil.dup
38
+ rescue TypeError
39
+ # +nil+ is not duplicable:
40
+ #
41
+ # nil.duplicable? # => false
42
+ # nil.dup # => TypeError: can't dup NilClass
43
+ def duplicable?
44
+ false
45
+ end
46
+ end
47
+ end
48
+
49
+ class FalseClass
50
+ begin
51
+ false.dup
52
+ rescue TypeError
53
+ # +false+ is not duplicable:
54
+ #
55
+ # false.duplicable? # => false
56
+ # false.dup # => TypeError: can't dup FalseClass
57
+ def duplicable?
58
+ false
59
+ end
60
+ end
61
+ end
62
+
63
+ class TrueClass
64
+ begin
65
+ true.dup
66
+ rescue TypeError
67
+ # +true+ is not duplicable:
68
+ #
69
+ # true.duplicable? # => false
70
+ # true.dup # => TypeError: can't dup TrueClass
71
+ def duplicable?
72
+ false
73
+ end
74
+ end
75
+ end
76
+
77
+ class Symbol
78
+ begin
79
+ :symbol.dup # Ruby 2.4.x.
80
+ "symbol_from_string".to_sym.dup # Some symbols can't `dup` in Ruby 2.4.0.
81
+ rescue TypeError
82
+ # Symbols are not duplicable:
83
+ #
84
+ # :my_symbol.duplicable? # => false
85
+ # :my_symbol.dup # => TypeError: can't dup Symbol
86
+ def duplicable?
87
+ false
88
+ end
89
+ end
90
+ end
91
+
92
+ class Numeric
93
+ begin
94
+ 1.dup
95
+ rescue TypeError
96
+ # Numbers are not duplicable:
97
+ #
98
+ # 3.duplicable? # => false
99
+ # 3.dup # => TypeError: can't dup Integer
100
+ def duplicable?
101
+ false
102
+ end
103
+ end
104
+ end
105
+
106
+ require "bigdecimal"
107
+ class BigDecimal
108
+ # BigDecimals are duplicable:
109
+ #
110
+ # BigDecimal("1.2").duplicable? # => true
111
+ # BigDecimal("1.2").dup # => #<BigDecimal:...,'0.12E1',18(18)>
112
+ def duplicable?
113
+ true
114
+ end
115
+ end
116
+
117
+ class Method
118
+ # Methods are not duplicable:
119
+ #
120
+ # method(:puts).duplicable? # => false
121
+ # method(:puts).dup # => TypeError: allocator undefined for Method
122
+ def duplicable?
123
+ false
124
+ end
125
+ end
126
+
127
+ class Complex
128
+ begin
129
+ Complex(1).dup
130
+ rescue TypeError
131
+ # Complexes are not duplicable:
132
+ #
133
+ # Complex(1).duplicable? # => false
134
+ # Complex(1).dup # => TypeError: can't copy Complex
135
+ def duplicable?
136
+ false
137
+ end
138
+ end
139
+ end
140
+
141
+ class Rational
142
+ begin
143
+ Rational(1).dup
144
+ rescue TypeError
145
+ # Rationals are not duplicable:
146
+ #
147
+ # Rational(1).duplicable? # => false
148
+ # Rational(1).dup # => TypeError: can't copy Rational
149
+ def duplicable?
150
+ false
151
+ end
152
+ end
153
+ end
data/lib/raven/event.rb CHANGED
@@ -1,234 +1,125 @@
1
1
  # frozen_string_literal: true
2
- require 'rubygems'
2
+
3
3
  require 'socket'
4
4
  require 'securerandom'
5
- require 'digest/md5'
6
-
7
- require 'raven/error'
8
- require 'raven/linecache'
9
5
 
10
6
  module Raven
11
7
  class Event
12
- LOG_LEVELS = {
13
- "debug" => 10,
14
- "info" => 20,
15
- "warn" => 30,
16
- "warning" => 30,
17
- "error" => 40,
18
- "fatal" => 50
19
- }.freeze
20
-
21
- BACKTRACE_RE = /^(.+?):(\d+)(?::in `(.+?)')?$/
22
-
23
- PLATFORM = "ruby".freeze
24
- SDK = { "name" => "sentry-raven", "version" => Raven::VERSION }.freeze
25
-
26
- attr_accessor :id, :timestamp, :time_spent, :level, :logger,
27
- :culprit, :server_name, :release, :modules, :extra, :tags,
28
- :context, :configuration, :checksum, :fingerprint, :environment,
29
- :server_os, :runtime, :breadcrumbs, :user, :backtrace
30
-
31
- def initialize(init = {})
32
- @configuration = Raven.configuration
33
- @interfaces = {}
34
- @breadcrumbs = Raven.breadcrumbs
35
- @context = Raven.context
36
- @id = SecureRandom.uuid.delete("-")
37
- @timestamp = Time.now.utc
38
- @time_spent = nil
39
- @level = :error
40
- @logger = ''
41
- @culprit = nil
42
- @server_name = @configuration.server_name
43
- @release = @configuration.release
44
- @modules = list_gem_specs if @configuration.send_modules
45
- @user = {} # TODO: contexts
46
- @extra = {} # TODO: contexts
47
- @server_os = {} # TODO: contexts
48
- @runtime = {} # TODO: contexts
49
- @tags = {} # TODO: contexts
50
- @checksum = nil
51
- @fingerprint = nil
52
- @environment = @configuration.current_environment
53
-
54
- yield self if block_given?
55
-
56
- if !self[:http] && @context.rack_env
57
- interface :http do |int|
58
- int.from_rack(@context.rack_env)
59
- end
8
+ # See Sentry server default limits at
9
+ # https://github.com/getsentry/sentry/blob/master/src/sentry/conf/server.py
10
+ MAX_MESSAGE_SIZE_IN_BYTES = 1024 * 8
11
+ REQUIRED_OPTION_KEYS = [:configuration, :context, :breadcrumbs].freeze
12
+
13
+ SDK = { "name" => "raven-ruby", "version" => Raven::VERSION }.freeze
14
+
15
+ attr_accessor :id, :logger, :transaction, :server_name, :release, :modules,
16
+ :extra, :tags, :context, :configuration, :checksum,
17
+ :fingerprint, :environment, :server_os, :runtime,
18
+ :breadcrumbs, :user, :backtrace, :platform, :sdk
19
+ alias event_id id
20
+
21
+ attr_reader :level, :timestamp, :time_spent
22
+
23
+ def initialize(options)
24
+ # Set some simple default values
25
+ self.id = SecureRandom.uuid.delete("-")
26
+ self.timestamp = Time.now.utc
27
+ self.level = :error
28
+ self.logger = :ruby
29
+ self.platform = :ruby
30
+ self.sdk = SDK
31
+
32
+ # Set some attributes with empty hashes to allow merging
33
+ @interfaces = {}
34
+ self.user = {} # TODO: contexts
35
+ self.extra = {} # TODO: contexts
36
+ self.server_os = {} # TODO: contexts
37
+ self.runtime = {} # TODO: contexts
38
+ self.tags = {} # TODO: contexts
39
+
40
+ unless REQUIRED_OPTION_KEYS.all? { |key| options.key?(key) }
41
+ raise "you must provide configuration, context, and breadcrumbs when initializing a Raven::Event"
60
42
  end
61
43
 
62
- if @context.rack_env # TODO: contexts
63
- @context.user[:ip_address] = calculate_real_ip_from_rack
64
- end
65
-
66
- init.each_pair { |key, val| public_send(key.to_s + "=", val) }
44
+ self.configuration = options[:configuration]
45
+ self.context = options[:context]
46
+ self.breadcrumbs = options[:breadcrumbs]
67
47
 
68
- @user = @context.user.merge(@user) # TODO: contexts
69
- @extra = @context.extra.merge(@extra) # TODO: contexts
70
- @tags = @configuration.tags.merge(@context.tags).merge(@tags) # TODO: contexts
71
- @server_os = @context.server_os # TODO: contexts
72
- @runtime = @context.runtime # TODO: contexts
48
+ # Allow attributes to be set on the event at initialization
49
+ yield self if block_given?
50
+ options.each_pair { |key, val| public_send("#{key}=", val) unless val.nil? }
73
51
 
74
- # Some type coercion
75
- @timestamp = @timestamp.strftime('%Y-%m-%dT%H:%M:%S') if @timestamp.is_a?(Time)
76
- @time_spent = (@time_spent * 1000).to_i if @time_spent.is_a?(Float)
77
- @level = LOG_LEVELS[@level.to_s.downcase] if @level.is_a?(String) || @level.is_a?(Symbol)
52
+ set_core_attributes_from_configuration
53
+ set_core_attributes_from_context
78
54
  end
79
55
 
80
- def message
81
- @interfaces[:logentry] && @interfaces[:logentry].unformatted_message
82
- end
83
-
84
- def message=(args)
85
- message, params = *args
86
- interface(:message) do |int|
87
- int.message = message
88
- int.params = params
56
+ def self.from_exception(exc, options = {}, &block)
57
+ exception_context = if exc.instance_variable_defined?(:@__raven_context)
58
+ exc.instance_variable_get(:@__raven_context)
59
+ elsif exc.respond_to?(:raven_context)
60
+ exc.raven_context
61
+ else
62
+ {}
63
+ end
64
+ options = Raven::Utils::DeepMergeHash.deep_merge(exception_context, options)
65
+
66
+ return unless options[:configuration].exception_class_allowed?(exc)
67
+
68
+ new(options) do |evt|
69
+ evt.add_exception_interface(exc)
70
+ yield evt if block
89
71
  end
90
72
  end
91
73
 
92
- class << self
93
- def from_exception(exc, options = {}, &block)
94
- exception_context = get_exception_context(exc) || {}
95
- options = Raven::Utils::DeepMergeHash.deep_merge(exception_context, options)
96
-
97
- configuration = options[:configuration] || Raven.configuration
98
- if exc.is_a?(Raven::Error)
99
- # Try to prevent error reporting loops
100
- Raven.logger.info "Refusing to capture Raven error: #{exc.inspect}"
101
- return nil
102
- end
103
- if configuration[:excluded_exceptions].any? { |x| get_exception_class(x) === exc }
104
- Raven.logger.info "User excluded error: #{exc.inspect}"
105
- return nil
106
- end
107
-
108
- new(options) do |evt|
109
- evt.configuration = configuration
110
- evt.message = "#{exc.class}: #{exc.message}"
111
- evt.level = options[:level] || :error
112
-
113
- add_exception_interface(evt, exc)
114
-
115
- yield evt if block
116
- end
117
- end
118
-
119
- def from_message(message, options = {})
120
- message = message.byteslice(0...10_000) # Messages limited to 10kb
121
- configuration = options[:configuration] || Raven.configuration
122
-
123
- new(options) do |evt|
124
- evt.configuration = configuration
125
- evt.level = options[:level] || :error
126
- evt.message = message, options[:message_params] || []
127
- if options[:backtrace]
128
- evt.interface(:stacktrace) do |int|
129
- stacktrace_interface_from(int, evt, options[:backtrace])
130
- end
74
+ def self.from_message(message, options = {})
75
+ new(options) do |evt|
76
+ evt.message = message, options[:message_params] || []
77
+ if options[:backtrace]
78
+ evt.interface(:stacktrace) do |int|
79
+ int.frames = evt.stacktrace_interface_from(options[:backtrace])
131
80
  end
132
81
  end
133
82
  end
83
+ end
134
84
 
135
- private
136
-
137
- def get_exception_class(x)
138
- x.is_a?(Module) ? x : qualified_const_get(x)
139
- end
140
-
141
- # In Ruby <2.0 const_get can't lookup "SomeModule::SomeClass" in one go
142
- def qualified_const_get(x)
143
- x = x.to_s
144
- parts = x.split("::")
145
- parts.reject!(&:empty?)
85
+ def message
86
+ @interfaces[:logentry]&.unformatted_message
87
+ end
146
88
 
147
- if parts.size < 2
148
- Object.const_get(x)
149
- else
150
- parts.inject(Object) { |a, e| a.const_get(e) }
151
- end
152
- rescue NameError # There's no way to safely ask if a constant exist for an unknown string
153
- nil
89
+ def message=(args)
90
+ if args.is_a?(Array)
91
+ message, params = args[0], args[0..-1]
92
+ else
93
+ message = args
154
94
  end
155
95
 
156
- def get_exception_context(exc)
157
- if exc.instance_variable_defined?(:@__raven_context)
158
- exc.instance_variable_get(:@__raven_context)
159
- elsif exc.respond_to?(:raven_context)
160
- exc.raven_context
161
- end
96
+ unless message.is_a?(String)
97
+ configuration.logger.debug("You're passing a non-string message")
98
+ message = message.to_s
162
99
  end
163
100
 
164
- def add_exception_interface(evt, exc)
165
- evt.interface(:exception) do |exc_int|
166
- exceptions = [exc]
167
- context = Set.new [exc.object_id]
168
- backtraces = Set.new
169
-
170
- while exc.respond_to?(:cause) && exc.cause
171
- exc = exc.cause
172
- break if context.include?(exc.object_id)
173
- exceptions << exc
174
- context.add(exc.object_id)
175
- end
176
- exceptions.reverse!
177
-
178
- exc_int.values = exceptions.map do |e|
179
- SingleExceptionInterface.new do |int|
180
- int.type = e.class.to_s
181
- int.value = e.to_s
182
- int.module = e.class.to_s.split('::')[0...-1].join('::')
183
-
184
- int.stacktrace =
185
- if e.backtrace && !backtraces.include?(e.backtrace.object_id)
186
- backtraces << e.backtrace.object_id
187
- StacktraceInterface.new do |stacktrace|
188
- stacktrace_interface_from(stacktrace, evt, e.backtrace)
189
- end
190
- end
191
- end
192
- end
193
- end
101
+ interface(:message) do |int|
102
+ int.message = message.byteslice(0...MAX_MESSAGE_SIZE_IN_BYTES) # Messages limited to 10kb
103
+ int.params = params
194
104
  end
105
+ end
195
106
 
196
- def stacktrace_interface_from(int, evt, backtrace)
197
- backtrace = Backtrace.parse(backtrace)
198
-
199
- int.frames = []
200
- backtrace.lines.reverse_each do |line|
201
- frame = StacktraceInterface::Frame.new
202
- frame.abs_path = line.file if line.file
203
- frame.function = line.method if line.method
204
- frame.lineno = line.number
205
- frame.in_app = line.in_app
206
- frame.module = line.module_name if line.module_name
207
-
208
- if evt.configuration[:context_lines] && frame.abs_path
209
- frame.pre_context, frame.context_line, frame.post_context = \
210
- evt.get_file_context(frame.abs_path, frame.lineno, evt.configuration[:context_lines])
211
- end
212
-
213
- int.frames << frame if frame.filename
214
- end
215
-
216
- evt.culprit = evt.get_culprit(int.frames)
217
- end
107
+ def timestamp=(time)
108
+ @timestamp = time.is_a?(Time) ? time.strftime('%Y-%m-%dT%H:%M:%S') : time
109
+ end
218
110
 
219
- # Because linecache can go to hell
220
- def _source_lines(_path, _from, _to)
221
- end
111
+ def time_spent=(time)
112
+ @time_spent = time.is_a?(Float) ? (time * 1000).to_i : time
222
113
  end
223
114
 
224
- def list_gem_specs
225
- # Older versions of Rubygems don't support iterating over all specs
226
- Hash[Gem::Specification.map { |spec| [spec.name, spec.version.to_s] }] if Gem::Specification.respond_to?(:map)
115
+ def level=(new_level) # needed to meet the Sentry spec
116
+ @level = new_level.to_s == "warn" ? :warning : new_level
227
117
  end
228
118
 
229
119
  def interface(name, value = nil, &block)
230
120
  int = Interface.registered[name]
231
121
  raise(Error, "Unknown interface: #{name}") unless int
122
+
232
123
  @interfaces[int.sentry_alias] = int.new(value, &block) if value || block
233
124
  @interfaces[int.sentry_alias]
234
125
  end
@@ -242,54 +133,63 @@ module Raven
242
133
  end
243
134
 
244
135
  def to_hash
245
- data = {
246
- :event_id => @id,
247
- :timestamp => @timestamp,
248
- :time_spent => @time_spent,
249
- :level => @level,
250
- :platform => PLATFORM,
251
- :sdk => SDK,
252
- :contexts => {
253
- :server_os => @server_os,
254
- :runtime => @runtime
255
- }
256
- }
257
-
258
- data[:logger] = @logger if @logger
259
- data[:culprit] = @culprit if @culprit
260
- data[:server_name] = @server_name if @server_name
261
- data[:release] = @release if @release
262
- data[:environment] = @environment if @environment
263
- data[:fingerprint] = @fingerprint if @fingerprint
264
- data[:modules] = @modules if @modules
265
- data[:extra] = @extra if @extra
266
- data[:tags] = @tags if @tags
267
- data[:user] = @user if @user
136
+ data = [:checksum, :environment, :event_id, :extra, :fingerprint, :level,
137
+ :logger, :message, :modules, :platform, :release, :sdk, :server_name,
138
+ :tags, :time_spent, :timestamp, :transaction, :user].each_with_object({}) do |att, memo|
139
+ memo[att] = public_send(att) if public_send(att)
140
+ end
141
+
268
142
  data[:breadcrumbs] = @breadcrumbs.to_hash unless @breadcrumbs.empty?
269
- data[:checksum] = @checksum if @checksum
270
143
 
271
144
  @interfaces.each_pair do |name, int_data|
272
145
  data[name.to_sym] = int_data.to_hash
273
146
  end
274
- data[:message] = message
275
147
  data
276
148
  end
277
149
 
278
- def get_file_context(filename, lineno, context)
279
- return nil, nil, nil unless Raven::LineCache.valid_file?(filename)
280
- lines = Array.new(2 * context + 1) do |i|
281
- Raven::LineCache.getline(filename, lineno - context + i)
282
- end
283
- [lines[0..(context - 1)], lines[context], lines[(context + 1)..-1]]
150
+ def to_json_compatible
151
+ cleaned_hash = async_json_processors.reduce(to_hash) { |a, e| e.process(a) }
152
+ JSON.parse(JSON.generate(cleaned_hash))
284
153
  end
285
154
 
286
- def get_culprit(frames)
287
- lastframe = frames.reverse.find(&:in_app) || frames.last
288
- "#{lastframe.filename} in #{lastframe.function} at line #{lastframe.lineno}" if lastframe
155
+ def add_exception_interface(exc)
156
+ interface(:exception) do |exc_int|
157
+ exceptions = Raven::Utils::ExceptionCauseChain.exception_to_array(exc).reverse
158
+ backtraces = Set.new
159
+ exc_int.values = exceptions.map do |e|
160
+ SingleExceptionInterface.new do |int|
161
+ int.type = e.class.to_s
162
+ int.value = e.to_s
163
+ int.module = e.class.to_s.split('::')[0...-1].join('::')
164
+
165
+ int.stacktrace =
166
+ if e.backtrace && !backtraces.include?(e.backtrace.object_id)
167
+ backtraces << e.backtrace.object_id
168
+ StacktraceInterface.new do |stacktrace|
169
+ stacktrace.frames = stacktrace_interface_from(e.backtrace)
170
+ end
171
+ end
172
+ end
173
+ end
174
+ end
289
175
  end
290
176
 
291
- def to_json_compatible
292
- JSON.parse(JSON.generate(to_hash))
177
+ def stacktrace_interface_from(backtrace)
178
+ Backtrace.parse(backtrace, { configuration: configuration }).lines.reverse.each_with_object([]) do |line, memo|
179
+ frame = StacktraceInterface::Frame.new
180
+ frame.abs_path = line.file if line.file
181
+ frame.function = line.method if line.method
182
+ frame.lineno = line.number
183
+ frame.in_app = line.in_app
184
+ frame.module = line.module_name if line.module_name
185
+
186
+ if configuration[:context_lines] && frame.abs_path
187
+ frame.pre_context, frame.context_line, frame.post_context = \
188
+ configuration.linecache.get_file_context(frame.abs_path, frame.lineno, configuration[:context_lines])
189
+ end
190
+
191
+ memo << frame if frame.filename
192
+ end
293
193
  end
294
194
 
295
195
  # For cross-language compat
@@ -302,6 +202,36 @@ module Raven
302
202
 
303
203
  private
304
204
 
205
+ def set_core_attributes_from_configuration
206
+ self.server_name ||= configuration.server_name
207
+ self.release ||= configuration.release
208
+ self.modules = list_gem_specs if configuration.send_modules
209
+ self.environment ||= configuration.current_environment
210
+ end
211
+
212
+ def set_core_attributes_from_context
213
+ self.transaction ||= context.transaction.last
214
+
215
+ # If this is a Rack event, merge Rack context
216
+ add_rack_context if !self[:http] && context.rack_env
217
+
218
+ # Merge contexts
219
+ self.user = context.user.merge(user) # TODO: contexts
220
+ self.extra = context.extra.merge(extra) # TODO: contexts
221
+ self.tags = configuration.tags.merge(context.tags).merge!(tags) # TODO: contexts
222
+ end
223
+
224
+ def add_rack_context
225
+ interface :http do |int|
226
+ int.from_rack(context.rack_env)
227
+ end
228
+ context.user[:ip_address] = calculate_real_ip_from_rack
229
+
230
+ if request_id = Utils::RequestId.read_from(context.rack_env)
231
+ context.tags[:request_id] = request_id
232
+ end
233
+ end
234
+
305
235
  # When behind a proxy (or if the user is using a proxy), we can't use
306
236
  # REMOTE_ADDR to determine the Event IP, and must use other headers instead.
307
237
  def calculate_real_ip_from_rack
@@ -312,5 +242,14 @@ module Raven
312
242
  :forwarded_for => context.rack_env["HTTP_X_FORWARDED_FOR"]
313
243
  ).calculate_ip
314
244
  end
245
+
246
+ def async_json_processors
247
+ configuration.processors.map { |v| v.new(self) }
248
+ end
249
+
250
+ def list_gem_specs
251
+ # Older versions of Rubygems don't support iterating over all specs
252
+ Hash[Gem::Specification.map { |spec| [spec.name, spec.version.to_s] }] if Gem::Specification.respond_to?(:map)
253
+ end
315
254
  end
316
255
  end
@@ -0,0 +1,17 @@
1
+ module DeprecationHelper
2
+ def self.deprecate_dasherized_filename(correct_filename)
3
+ warn "[Deprecation Warning] Dasherized filename \"#{correct_filename.gsub('_', '-')}\" is deprecated and will be removed in 4.0; use \"#{correct_filename}\" instead" # rubocop:disable Style/LineLength
4
+ end
5
+
6
+ def self.deprecate_old_breadcrumbs_configuration(logger)
7
+ deprecated_usage =
8
+ if logger == :sentry_logger
9
+ "require \"raven/breadcrumbs/logger\""
10
+ else
11
+ "Raven.configuration.rails_activesupport_breadcrumbs = true"
12
+ end
13
+ recommended_usage = "Raven.configuration.breadcrumbs_logger = :#{logger}"
14
+
15
+ warn "[Deprecation Warning] The way you enable breadcrumbs logger (#{deprecated_usage}) is deprecated and will be removed in 4.0; use '#{recommended_usage}' instead" # rubocop:disable Style/LineLength
16
+ end
17
+ end