sentry-raven 1.1.0 → 3.1.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 (69) 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 +17 -8
  11. data/lib/raven/base.rb +45 -194
  12. data/lib/raven/breadcrumbs/active_support_logger.rb +25 -0
  13. data/lib/raven/breadcrumbs/logger.rb +3 -0
  14. data/lib/raven/breadcrumbs/sentry_logger.rb +73 -0
  15. data/lib/raven/breadcrumbs.rb +76 -0
  16. data/lib/raven/cli.rb +31 -39
  17. data/lib/raven/client.rb +45 -32
  18. data/lib/raven/configuration.rb +427 -130
  19. data/lib/raven/context.rb +33 -6
  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 +194 -206
  23. data/lib/raven/helpers/deprecation_helper.rb +17 -0
  24. data/lib/raven/instance.rb +249 -0
  25. data/lib/raven/integrations/delayed_job.rb +25 -23
  26. data/lib/raven/integrations/rack-timeout.rb +22 -0
  27. data/lib/raven/integrations/rack.rb +40 -24
  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 +39 -7
  33. data/lib/raven/integrations/rake.rb +7 -2
  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 -48
  37. data/lib/raven/integrations/tasks.rb +1 -1
  38. data/lib/raven/interface.rb +25 -0
  39. data/lib/raven/interfaces/exception.rb +5 -8
  40. data/lib/raven/interfaces/http.rb +5 -12
  41. data/lib/raven/interfaces/message.rb +10 -6
  42. data/lib/raven/interfaces/single_exception.rb +1 -5
  43. data/lib/raven/interfaces/stack_trace.rb +23 -30
  44. data/lib/raven/linecache.rb +35 -23
  45. data/lib/raven/logger.rb +13 -16
  46. data/lib/raven/processor/cookies.rb +27 -7
  47. data/lib/raven/processor/http_headers.rb +55 -0
  48. data/lib/raven/processor/post_data.rb +16 -3
  49. data/lib/raven/processor/removecircularreferences.rb +12 -8
  50. data/lib/raven/processor/removestacktrace.rb +17 -6
  51. data/lib/raven/processor/sanitizedata.rb +92 -37
  52. data/lib/raven/processor/utf8conversion.rb +39 -14
  53. data/lib/raven/processor.rb +5 -1
  54. data/lib/raven/transports/http.rb +31 -22
  55. data/lib/raven/transports/stdout.rb +20 -0
  56. data/lib/raven/transports.rb +6 -10
  57. data/lib/raven/utils/context_filter.rb +42 -0
  58. data/lib/raven/utils/deep_merge.rb +6 -12
  59. data/lib/raven/utils/exception_cause_chain.rb +20 -0
  60. data/lib/raven/utils/real_ip.rb +62 -0
  61. data/lib/raven/utils/request_id.rb +16 -0
  62. data/lib/raven/version.rb +2 -1
  63. data/lib/sentry-raven-without-integrations.rb +6 -1
  64. data/lib/sentry_raven_without_integrations.rb +1 -0
  65. data/sentry-raven.gemspec +28 -0
  66. metadata +44 -127
  67. data/lib/raven/error.rb +0 -4
  68. data/lib/raven/interfaces.rb +0 -34
  69. data/lib/raven/okjson.rb +0 -614
@@ -1,260 +1,557 @@
1
- require 'logger'
2
1
  require 'uri'
3
2
 
4
3
  module Raven
5
4
  class Configuration
6
- # Simple server string (setter provided below)
7
- attr_reader :server
5
+ # Directories to be recognized as part of your app. e.g. if you
6
+ # have an `engines` dir at the root of your project, you may want
7
+ # to set this to something like /(app|config|engines|lib)/
8
+ attr_accessor :app_dirs_pattern
8
9
 
9
- # Public key for authentication with the Sentry server
10
- attr_accessor :public_key
10
+ # Provide an object that responds to `call` to send events asynchronously.
11
+ # E.g.: lambda { |event| Thread.new { Raven.send_event(event) } }
12
+ attr_reader :async
13
+ alias async? async
11
14
 
12
- # Secret key for authentication with the Sentry server
13
- attr_accessor :secret_key
15
+ # An array of breadcrumbs loggers to be used. Available options are:
16
+ # - :sentry_logger
17
+ # - :active_support_logger
18
+ attr_reader :breadcrumbs_logger
14
19
 
15
- # Accessors for the component parts of the DSN
16
- attr_accessor :scheme
20
+ # Number of lines of code context to capture, or nil for none
21
+ attr_accessor :context_lines
22
+
23
+ # RACK_ENV by default.
24
+ attr_reader :current_environment
25
+
26
+ # Encoding type for event bodies. Must be :json or :gzip.
27
+ attr_reader :encoding
28
+
29
+ # Whitelist of environments that will send notifications to Sentry. Array of Strings.
30
+ attr_accessor :environments
31
+
32
+ # Logger 'progname's to exclude from breadcrumbs
33
+ attr_accessor :exclude_loggers
34
+
35
+ # Array of exception classes that should never be sent. See IGNORE_DEFAULT.
36
+ # You should probably append to this rather than overwrite it.
37
+ attr_accessor :excluded_exceptions
38
+
39
+ # Boolean to check nested exceptions when deciding if to exclude. Defaults to false
40
+ attr_accessor :inspect_exception_causes_for_exclusion
41
+ alias inspect_exception_causes_for_exclusion? inspect_exception_causes_for_exclusion
42
+
43
+ # DSN component - set automatically if DSN provided
17
44
  attr_accessor :host
18
- attr_accessor :port
45
+
46
+ # The Faraday adapter to be used. Will default to Net::HTTP when not set.
47
+ attr_accessor :http_adapter
48
+
49
+ # A Proc yeilding the faraday builder allowing for further configuration
50
+ # of the faraday adapter
51
+ attr_accessor :faraday_builder
52
+
53
+ # You may provide your own LineCache for matching paths with source files.
54
+ # This may be useful if you need to get source code from places other than
55
+ # the disk. See Raven::LineCache for the required interface you must implement.
56
+ attr_accessor :linecache
57
+
58
+ # Logger used by Raven. In Rails, this is the Rails logger, otherwise
59
+ # Raven provides its own Raven::Logger.
60
+ attr_accessor :logger
61
+
62
+ # Timeout waiting for the Sentry server connection to open in seconds
63
+ attr_accessor :open_timeout
64
+
65
+ # DSN component - set automatically if DSN provided
19
66
  attr_accessor :path
20
67
 
68
+ # DSN component - set automatically if DSN provided
69
+ attr_accessor :port
70
+
71
+ # Processors to run on data before sending upstream. See DEFAULT_PROCESSORS.
72
+ # You should probably append to this rather than overwrite it.
73
+ attr_accessor :processors
74
+
21
75
  # Project ID number to send to the Sentry server
76
+ # If you provide a DSN, this will be set automatically.
22
77
  attr_accessor :project_id
23
78
 
24
- # Project directory root
25
- attr_accessor :project_root
26
-
27
- # Encoding type for event bodies
28
- attr_reader :encoding
79
+ # Project directory root for in_app detection. Could be Rails root, etc.
80
+ # Set automatically for Rails.
81
+ attr_reader :project_root
29
82
 
30
- # Logger to use internally
31
- attr_accessor :logger
83
+ # Proxy information to pass to the HTTP adapter (via Faraday)
84
+ attr_accessor :proxy
32
85
 
33
- # Silence ready message
34
- attr_accessor :silence_ready
86
+ # Public key for authentication with the Sentry server
87
+ # If you provide a DSN, this will be set automatically.
88
+ attr_accessor :public_key
35
89
 
36
- # Number of lines of code context to capture, or nil for none
37
- attr_accessor :context_lines
90
+ # Turns on ActiveSupport breadcrumbs integration
91
+ attr_reader :rails_activesupport_breadcrumbs
38
92
 
39
- # Whitelist of environments that will send notifications to Sentry
40
- attr_accessor :environments
93
+ # Rails catches exceptions in the ActionDispatch::ShowExceptions or
94
+ # ActionDispatch::DebugExceptions middlewares, depending on the environment.
95
+ # When `rails_report_rescued_exceptions` is true (it is by default), Raven
96
+ # will report exceptions even when they are rescued by these middlewares.
97
+ attr_accessor :rails_report_rescued_exceptions
41
98
 
42
- # Include module versions in reports?
43
- attr_accessor :send_modules
99
+ # Release tag to be passed with every event sent to Sentry.
100
+ # We automatically try to set this to a git SHA or Capistrano release.
101
+ attr_accessor :release
44
102
 
45
- # Which exceptions should never be sent
46
- attr_accessor :excluded_exceptions
103
+ # The sampling factor to apply to events. A value of 0.0 will not send
104
+ # any events, and a value of 1.0 will send 100% of events.
105
+ attr_accessor :sample_rate
47
106
 
48
- # Processors to run on data before sending upstream
49
- attr_accessor :processors
107
+ # Boolean - sanitize values that look like credit card numbers
108
+ attr_accessor :sanitize_credit_cards
50
109
 
51
- # Timeout when waiting for the server to return data in seconds
52
- attr_accessor :timeout
110
+ # By default, Sentry censors Hash values when their keys match things like
111
+ # "secret", "password", etc. Provide an array of Strings that, when matched in
112
+ # a hash key, will be censored and not sent to Sentry.
113
+ attr_accessor :sanitize_fields
53
114
 
54
- # Timeout waiting for the connection to open in seconds
55
- attr_accessor :open_timeout
115
+ # If you're sure you want to override the default sanitization values, you can
116
+ # add to them to an array of Strings here, e.g. %w(authorization password)
117
+ attr_accessor :sanitize_fields_excluded
56
118
 
57
- # Should the SSL certificate of the server be verified?
58
- attr_accessor :ssl_verification
119
+ # Sanitize additional HTTP headers - only Authorization is removed by default.
120
+ attr_accessor :sanitize_http_headers
59
121
 
60
- # The path to the SSL certificate file
61
- attr_accessor :ssl_ca_file
122
+ # DSN component - set automatically if DSN provided.
123
+ # Otherwise, can be one of "http", "https", or "dummy"
124
+ attr_accessor :scheme
62
125
 
63
- # SSl settings passed direactly to faraday's ssl option
64
- attr_accessor :ssl
126
+ # a proc/lambda that takes an array of stack traces
127
+ # it'll be used to silence (reduce) backtrace of the exception
128
+ #
129
+ # for example:
130
+ #
131
+ # ```ruby
132
+ # Raven.configuration.backtrace_cleanup_callback = lambda do |backtrace|
133
+ # Rails.backtrace_cleaner.clean(backtrace)
134
+ # end
135
+ # ```
136
+ #
137
+ attr_accessor :backtrace_cleanup_callback
65
138
 
66
- # Proxy information to pass to the HTTP adapter
67
- attr_accessor :proxy
139
+ # Secret key for authentication with the Sentry server
140
+ # If you provide a DSN, this will be set automatically.
141
+ #
142
+ # This is deprecated and not necessary for newer Sentry installations any more.
143
+ attr_accessor :secret_key
68
144
 
69
- attr_reader :current_environment
145
+ # Include module versions in reports - boolean.
146
+ attr_accessor :send_modules
70
147
 
71
- # The Faraday adapter to be used. Will default to Net::HTTP when not set.
72
- attr_accessor :http_adapter
148
+ # Simple server string - set this to the DSN found on your Sentry settings.
149
+ attr_reader :server
73
150
 
74
151
  attr_accessor :server_name
75
152
 
76
- attr_accessor :release
153
+ # Provide a configurable callback to determine event capture.
154
+ # Note that the object passed into the block will be a String (messages) or
155
+ # an exception.
156
+ # e.g. lambda { |exc_or_msg| exc_or_msg.some_attr == false }
157
+ attr_reader :should_capture
77
158
 
78
- # DEPRECATED: This option is now ignored as we use our own adapter.
79
- attr_accessor :json_adapter
159
+ # Silences ready message when true.
160
+ attr_accessor :silence_ready
161
+
162
+ # SSL settings passed directly to Faraday's ssl option
163
+ attr_accessor :ssl
164
+
165
+ # The path to the SSL certificate file
166
+ attr_accessor :ssl_ca_file
167
+
168
+ # Should the SSL certificate of the server be verified?
169
+ attr_accessor :ssl_verification
80
170
 
81
- # Default tags for events
171
+ # Default tags for events. Hash.
82
172
  attr_accessor :tags
83
173
 
84
- # Optional Proc to be used to send events asynchronously.
85
- attr_reader :async
174
+ # Timeout when waiting for the server to return data in seconds.
175
+ attr_accessor :timeout
86
176
 
87
177
  # Optional Proc, called when the Sentry server cannot be contacted for any reason
88
- attr_accessor :transport_failure_callback
89
-
90
- # Directories to be recognized as part of your app. e.g. if you
91
- # have an `engines` dir at the root of your project, you may want
92
- # to set this to something like /(app|config|engines|lib)/
93
- attr_accessor :app_dirs_pattern
178
+ # E.g. lambda { |event| Thread.new { MyJobProcessor.send_email(event) } }
179
+ attr_reader :transport_failure_callback
94
180
 
95
- # Rails catches exceptions in the ActionDispatch::ShowExceptions or
96
- # ActionDispatch::DebugExceptions middlewares, depending on the environment.
97
- # When `rails_report_rescued_exceptions` is true (it is by default), Raven
98
- # will report exceptions even when they are rescued by these middlewares.
99
- attr_accessor :rails_report_rescued_exceptions
100
- # Deprecated accessor
101
- attr_accessor :catch_debugged_exceptions
181
+ # Optional Proc, called before sending an event to the server/
182
+ # E.g.: lambda { |event, hint| event }
183
+ # E.g.: lambda { |event, hint| nil }
184
+ # E.g.: lambda { |event, hint|
185
+ # event[:message] = 'a'
186
+ # event
187
+ # }
188
+ attr_reader :before_send
102
189
 
103
- # Provide a configurable callback to determine event capture
104
- attr_accessor :should_capture
190
+ # Errors object - an Array that contains error messages. See #
191
+ attr_reader :errors
105
192
 
106
- # additional fields to sanitize
107
- attr_accessor :sanitize_fields
193
+ # the dsn value, whether it's set via `config.dsn=` or `ENV["SENTRY_DSN"]`
194
+ attr_reader :dsn
108
195
 
109
- # Sanitize values that look like credit card numbers
110
- attr_accessor :sanitize_credit_cards
196
+ # Array of rack env parameters to be included in the event sent to sentry.
197
+ attr_accessor :rack_env_whitelist
111
198
 
199
+ # Most of these errors generate 4XX responses. In general, Sentry clients
200
+ # only automatically report 5xx responses.
112
201
  IGNORE_DEFAULT = [
113
202
  'AbstractController::ActionNotFound',
203
+ 'ActionController::BadRequest',
114
204
  'ActionController::InvalidAuthenticityToken',
205
+ 'ActionController::InvalidCrossOriginRequest',
206
+ 'ActionController::MethodNotAllowed',
207
+ 'ActionController::NotImplemented',
208
+ 'ActionController::ParameterMissing',
115
209
  'ActionController::RoutingError',
116
210
  'ActionController::UnknownAction',
211
+ 'ActionController::UnknownFormat',
212
+ 'ActionController::UnknownHttpMethod',
213
+ 'ActionDispatch::Http::Parameters::ParseError',
214
+ 'ActiveJob::DeserializationError', # Can cause infinite loops
117
215
  'ActiveRecord::RecordNotFound',
118
216
  'CGI::Session::CookieStore::TamperedWithCookie',
119
217
  'Mongoid::Errors::DocumentNotFound',
120
- 'Sinatra::NotFound',
218
+ 'Rack::QueryParser::InvalidParameterError',
219
+ 'Rack::QueryParser::ParameterTypeError',
220
+ 'Sinatra::NotFound'
121
221
  ].freeze
122
222
 
223
+ # Note the order - we have to remove circular references and bad characters
224
+ # before passing to other processors.
123
225
  DEFAULT_PROCESSORS = [
124
226
  Raven::Processor::RemoveCircularReferences,
125
227
  Raven::Processor::UTF8Conversion,
126
228
  Raven::Processor::SanitizeData,
127
229
  Raven::Processor::Cookies,
128
230
  Raven::Processor::PostData,
231
+ Raven::Processor::HTTPHeaders
129
232
  ].freeze
130
233
 
234
+ HEROKU_DYNO_METADATA_MESSAGE = "You are running on Heroku but haven't enabled Dyno Metadata. For Sentry's "\
235
+ "release detection to work correctly, please run `heroku labs:enable runtime-dyno-metadata`".freeze
236
+
237
+ RACK_ENV_WHITELIST_DEFAULT = %w(
238
+ REMOTE_ADDR
239
+ SERVER_NAME
240
+ SERVER_PORT
241
+ ).freeze
242
+
243
+ LOG_PREFIX = "** [Raven] ".freeze
244
+ MODULE_SEPARATOR = "::".freeze
245
+
246
+ AVAILABLE_BREADCRUMBS_LOGGERS = [:sentry_logger, :active_support_logger].freeze
247
+
131
248
  def initialize
132
- self.server = ENV['SENTRY_DSN'] if ENV['SENTRY_DSN']
133
- @context_lines = 3
134
- self.current_environment = ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'default'
135
- self.send_modules = true
249
+ self.async = false
250
+ self.breadcrumbs_logger = []
251
+ self.context_lines = 3
252
+ self.current_environment = current_environment_from_env
253
+ self.encoding = 'gzip'
254
+ self.environments = []
255
+ self.exclude_loggers = []
136
256
  self.excluded_exceptions = IGNORE_DEFAULT.dup
257
+ self.inspect_exception_causes_for_exclusion = false
258
+ self.linecache = ::Raven::LineCache.new
259
+ self.logger = ::Raven::Logger.new(STDOUT)
260
+ self.open_timeout = 1
137
261
  self.processors = DEFAULT_PROCESSORS.dup
262
+ self.project_root = detect_project_root
263
+ @rails_activesupport_breadcrumbs = false
264
+
265
+ self.rails_report_rescued_exceptions = true
266
+ self.release = detect_release
267
+ self.sample_rate = 1.0
268
+ self.sanitize_credit_cards = true
269
+ self.sanitize_fields = []
270
+ self.sanitize_fields_excluded = []
271
+ self.sanitize_http_headers = []
272
+ self.send_modules = true
273
+ self.server = ENV['SENTRY_DSN']
274
+ self.server_name = server_name_from_env
275
+ self.should_capture = false
138
276
  self.ssl_verification = true
139
- self.encoding = 'gzip'
140
- self.timeout = 1
141
- self.open_timeout = 1
142
- self.proxy = nil
143
277
  self.tags = {}
144
- self.async = false
145
- self.rails_report_rescued_exceptions = true
278
+ self.timeout = 2
146
279
  self.transport_failure_callback = false
147
- self.sanitize_fields = []
148
- self.sanitize_credit_cards = true
149
- self.environments = []
150
-
151
- self.release = detect_release
152
-
153
- # Try to resolve the hostname to an FQDN, but fall back to whatever the load name is
154
- self.server_name = Socket.gethostname
155
- self.server_name = Socket.gethostbyname(hostname).first rescue server_name
280
+ self.before_send = false
281
+ self.rack_env_whitelist = RACK_ENV_WHITELIST_DEFAULT
156
282
  end
157
283
 
158
284
  def server=(value)
285
+ return if value.nil?
286
+
287
+ @dsn = value
288
+
159
289
  uri = URI.parse(value)
160
290
  uri_path = uri.path.split('/')
161
291
 
162
292
  if uri.user
163
293
  # DSN-style string
164
- @project_id = uri_path.pop
165
- @public_key = uri.user
166
- @secret_key = uri.password
294
+ self.project_id = uri_path.pop
295
+ self.public_key = uri.user
296
+ self.secret_key = !(uri.password.nil? || uri.password.empty?) ? uri.password : nil
167
297
  end
168
298
 
169
- @scheme = uri.scheme
170
- @host = uri.host
171
- @port = uri.port if uri.port
172
- @path = uri_path.join('/')
299
+ self.scheme = uri.scheme
300
+ self.host = uri.host
301
+ self.port = uri.port if uri.port
302
+ self.path = uri_path.join('/')
173
303
 
174
304
  # For anyone who wants to read the base server string
175
- @server = "#{@scheme}://#{@host}"
176
- @server << ":#{@port}" unless @port == { 'http' => 80, 'https' => 443 }[@scheme]
177
- @server << @path
305
+ @server = "#{scheme}://#{host}"
306
+ @server += ":#{port}" unless port == { 'http' => 80, 'https' => 443 }[scheme]
307
+ @server += path
178
308
  end
309
+ alias dsn= server=
179
310
 
180
311
  def encoding=(encoding)
181
- raise Error.new('Unsupported encoding') unless %w(gzip json).include? encoding
312
+ raise(Error, 'Unsupported encoding') unless %w(gzip json).include? encoding
313
+
182
314
  @encoding = encoding
183
315
  end
184
316
 
185
- alias dsn= server=
186
-
187
317
  def async=(value)
188
- raise ArgumentError.new("async must be callable (or false to disable)") unless value == false || value.respond_to?(:call)
318
+ unless value == false || value.respond_to?(:call)
319
+ raise(ArgumentError, "async must be callable (or false to disable)")
320
+ end
321
+
189
322
  @async = value
190
323
  end
191
324
 
192
- alias async? async
325
+ def breadcrumbs_logger=(logger)
326
+ loggers =
327
+ if logger.is_a?(Array)
328
+ logger
329
+ else
330
+ unless AVAILABLE_BREADCRUMBS_LOGGERS.include?(logger)
331
+ raise Raven::Error, "Unsupported breadcrumbs logger. Supported loggers: #{AVAILABLE_BREADCRUMBS_LOGGERS}"
332
+ end
333
+
334
+ Array(logger)
335
+ end
336
+
337
+ require "raven/breadcrumbs/sentry_logger" if loggers.include?(:sentry_logger)
338
+
339
+ @breadcrumbs_logger = logger
340
+ end
193
341
 
194
342
  def transport_failure_callback=(value)
195
- raise ArgumentError.new("transport_failure_callback must be callable (or false to disable)") unless value == false || value.respond_to?(:call)
343
+ unless value == false || value.respond_to?(:call)
344
+ raise(ArgumentError, "transport_failure_callback must be callable (or false to disable)")
345
+ end
346
+
196
347
  @transport_failure_callback = value
197
348
  end
198
349
 
350
+ def should_capture=(value)
351
+ unless value == false || value.respond_to?(:call)
352
+ raise ArgumentError, "should_capture must be callable (or false to disable)"
353
+ end
354
+
355
+ @should_capture = value
356
+ end
357
+
358
+ def before_send=(value)
359
+ unless value == false || value.respond_to?(:call)
360
+ raise ArgumentError, "before_send must be callable (or false to disable)"
361
+ end
362
+
363
+ @before_send = value
364
+ end
365
+
199
366
  # Allows config options to be read like a hash
200
367
  #
201
368
  # @param [Symbol] option Key for a given attribute
202
369
  def [](option)
203
- send(option)
370
+ public_send(option)
204
371
  end
205
372
 
206
373
  def current_environment=(environment)
207
374
  @current_environment = environment.to_s
208
375
  end
209
376
 
210
- def send_in_current_environment?
211
- !!server && (environments.empty? || environments.include?(current_environment))
377
+ def capture_allowed?(message_or_exc = nil)
378
+ @errors = []
379
+
380
+ valid? &&
381
+ capture_in_current_environment? &&
382
+ capture_allowed_by_callback?(message_or_exc) &&
383
+ sample_allowed?
212
384
  end
385
+ # If we cannot capture, we cannot send.
386
+ alias sending_allowed? capture_allowed?
213
387
 
214
- def log_excluded_environment_message
215
- Raven.logger.debug "Event not sent due to excluded environment: #{current_environment}"
388
+ def error_messages
389
+ @errors = [errors[0]] + errors[1..-1].map(&:downcase) # fix case of all but first
390
+ errors.join(", ")
216
391
  end
217
392
 
218
- def verify!
219
- raise Error.new('No server specified') unless server
220
- raise Error.new('No public key specified') unless public_key
221
- raise Error.new('No secret key specified') unless secret_key
222
- raise Error.new('No project ID specified') unless project_id
393
+ def project_root=(root_dir)
394
+ @project_root = root_dir
395
+ Backtrace::Line.instance_variable_set(:@in_app_pattern, nil) # blow away cache
396
+ end
397
+
398
+ def rails_activesupport_breadcrumbs=(val)
399
+ DeprecationHelper.deprecate_old_breadcrumbs_configuration(:active_support_logger)
400
+ @rails_activesupport_breadcrumbs = val
401
+ end
402
+
403
+ def exception_class_allowed?(exc)
404
+ if exc.is_a?(Raven::Error)
405
+ # Try to prevent error reporting loops
406
+ logger.debug "Refusing to capture Raven error: #{exc.inspect}"
407
+ false
408
+ elsif excluded_exception?(exc)
409
+ logger.debug "User excluded error: #{exc.inspect}"
410
+ false
411
+ else
412
+ true
413
+ end
414
+ end
415
+
416
+ def enabled_in_current_env?
417
+ environments.empty? || environments.include?(current_environment)
418
+ end
419
+
420
+ private
421
+
422
+ def detect_project_root
423
+ if defined? Rails.root # we are in a Rails application
424
+ Rails.root.to_s
425
+ else
426
+ Dir.pwd
427
+ end
223
428
  end
224
429
 
225
430
  def detect_release
226
- detect_release_from_heroku ||
431
+ detect_release_from_env ||
432
+ detect_release_from_git ||
227
433
  detect_release_from_capistrano ||
228
- detect_release_from_git
434
+ detect_release_from_heroku
435
+ rescue => e
436
+ logger.error "Error detecting release: #{e.message}"
229
437
  end
230
438
 
231
- def project_root=(root_dir)
232
- @project_root = root_dir
233
- Backtrace::Line.instance_variable_set(:@in_app_pattern, nil) # blow away cache
439
+ def excluded_exception?(incoming_exception)
440
+ excluded_exceptions.any? do |excluded_exception|
441
+ matches_exception?(get_exception_class(excluded_exception), incoming_exception)
442
+ end
234
443
  end
235
444
 
236
- def catch_debugged_exceptions=(boolean)
237
- Raven.logger.warn "DEPRECATION WARNING: catch_debugged_exceptions has been \
238
- renamed to rails_report_rescued_exceptions. catch_debugged_exceptions will \
239
- be removed in raven-ruby 0.17.0"
240
- self.rails_report_rescued_exceptions = boolean
445
+ def get_exception_class(x)
446
+ x.is_a?(Module) ? x : qualified_const_get(x)
241
447
  end
242
448
 
243
- private
449
+ def matches_exception?(excluded_exception_class, incoming_exception)
450
+ if inspect_exception_causes_for_exclusion?
451
+ Raven::Utils::ExceptionCauseChain.exception_to_array(incoming_exception).any? { |cause| excluded_exception_class === cause }
452
+ else
453
+ excluded_exception_class === incoming_exception
454
+ end
455
+ end
456
+
457
+ # In Ruby <2.0 const_get can't lookup "SomeModule::SomeClass" in one go
458
+ def qualified_const_get(x)
459
+ x = x.to_s
460
+ if !x.match(/::/)
461
+ Object.const_get(x)
462
+ else
463
+ x.split(MODULE_SEPARATOR).reject(&:empty?).inject(Object) { |a, e| a.const_get(e) }
464
+ end
465
+ rescue NameError # There's no way to safely ask if a constant exist for an unknown string
466
+ nil
467
+ end
244
468
 
245
469
  def detect_release_from_heroku
470
+ return unless running_on_heroku?
471
+ return if ENV['CI']
472
+ logger.warn(HEROKU_DYNO_METADATA_MESSAGE) && return unless ENV['HEROKU_SLUG_COMMIT']
473
+
246
474
  ENV['HEROKU_SLUG_COMMIT']
247
475
  end
248
476
 
477
+ def running_on_heroku?
478
+ File.directory?("/etc/heroku")
479
+ end
480
+
249
481
  def detect_release_from_capistrano
250
- version = File.read(File.join(project_root, 'REVISION')).strip rescue nil
482
+ revision_file = File.join(project_root, 'REVISION')
483
+ revision_log = File.join(project_root, '..', 'revisions.log')
251
484
 
252
- # Capistrano 3.0 - 3.1.x
253
- version || File.open(File.join(project_root, '..', 'revisions.log')).to_a.last.strip.sub(/.*as release ([0-9]+).*/, '\1') rescue nil
485
+ if File.exist?(revision_file)
486
+ File.read(revision_file).strip
487
+ elsif File.exist?(revision_log)
488
+ File.open(revision_log).to_a.last.strip.sub(/.*as release ([0-9]+).*/, '\1')
489
+ end
254
490
  end
255
491
 
256
492
  def detect_release_from_git
257
- `git rev-parse --short HEAD`.strip if File.directory?(".git") rescue nil
493
+ Raven.sys_command("git rev-parse --short HEAD") if File.directory?(".git")
494
+ end
495
+
496
+ def detect_release_from_env
497
+ ENV['SENTRY_RELEASE']
498
+ end
499
+
500
+ def capture_in_current_environment?
501
+ return true if enabled_in_current_env?
502
+
503
+ @errors << "Not configured to send/capture in environment '#{current_environment}'"
504
+ false
505
+ end
506
+
507
+ def capture_allowed_by_callback?(message_or_exc)
508
+ return true if !should_capture || message_or_exc.nil? || should_capture.call(message_or_exc)
509
+
510
+ @errors << "should_capture returned false"
511
+ false
512
+ end
513
+
514
+ def valid?
515
+ return true if %w(server host path public_key project_id).all? { |k| public_send(k) }
516
+
517
+ if server
518
+ %w(server host path public_key project_id).map do |key|
519
+ @errors << "No #{key} specified" unless public_send(key)
520
+ end
521
+ else
522
+ @errors << "DSN not set"
523
+ end
524
+ false
525
+ end
526
+
527
+ def sample_allowed?
528
+ return true if sample_rate == 1.0
529
+
530
+ if Random::DEFAULT.rand >= sample_rate
531
+ @errors << "Excluded by random sample"
532
+ false
533
+ else
534
+ true
535
+ end
536
+ end
537
+
538
+ # Try to resolve the hostname to an FQDN, but fall back to whatever
539
+ # the load name is.
540
+ def resolve_hostname
541
+ Socket.gethostname ||
542
+ Socket.gethostbyname(hostname).first rescue server_name
543
+ end
544
+
545
+ def current_environment_from_env
546
+ ENV['SENTRY_CURRENT_ENV'] || ENV['SENTRY_ENVIRONMENT'] || ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'default'
547
+ end
548
+
549
+ def server_name_from_env
550
+ if running_on_heroku?
551
+ ENV['DYNO']
552
+ else
553
+ resolve_hostname
554
+ end
258
555
  end
259
556
  end
260
557
  end