jones-gem 2.7.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) 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/LICENSE +201 -0
  7. data/README.md +132 -0
  8. data/Rakefile +29 -0
  9. data/changelog.md +442 -0
  10. data/docs/Makefile +130 -0
  11. data/docs/breadcrumbs.rst +51 -0
  12. data/docs/conf.py +228 -0
  13. data/docs/config.rst +260 -0
  14. data/docs/context.rst +141 -0
  15. data/docs/index.rst +113 -0
  16. data/docs/install.rst +40 -0
  17. data/docs/integrations/heroku.rst +11 -0
  18. data/docs/integrations/index.rst +59 -0
  19. data/docs/integrations/puma.rst +30 -0
  20. data/docs/integrations/rack.rst +27 -0
  21. data/docs/integrations/rails.rst +84 -0
  22. data/docs/make.bat +155 -0
  23. data/docs/processors.rst +124 -0
  24. data/docs/sentry-doc-config.json +31 -0
  25. data/docs/usage.rst +176 -0
  26. data/exe/raven +32 -0
  27. data/jones-gem.gemspec +22 -0
  28. data/lib/raven.rb +3 -0
  29. data/lib/raven/backtrace.rb +137 -0
  30. data/lib/raven/base.rb +106 -0
  31. data/lib/raven/breadcrumbs.rb +76 -0
  32. data/lib/raven/breadcrumbs/activesupport.rb +19 -0
  33. data/lib/raven/breadcrumbs/logger.rb +93 -0
  34. data/lib/raven/cli.rb +59 -0
  35. data/lib/raven/client.rb +142 -0
  36. data/lib/raven/configuration.rb +434 -0
  37. data/lib/raven/context.rb +43 -0
  38. data/lib/raven/event.rb +259 -0
  39. data/lib/raven/instance.rb +221 -0
  40. data/lib/raven/integrations/delayed_job.rb +58 -0
  41. data/lib/raven/integrations/rack-timeout.rb +19 -0
  42. data/lib/raven/integrations/rack.rb +139 -0
  43. data/lib/raven/integrations/rails.rb +79 -0
  44. data/lib/raven/integrations/rails/active_job.rb +55 -0
  45. data/lib/raven/integrations/rails/controller_methods.rb +13 -0
  46. data/lib/raven/integrations/rails/controller_transaction.rb +13 -0
  47. data/lib/raven/integrations/rails/overrides/debug_exceptions_catcher.rb +31 -0
  48. data/lib/raven/integrations/rails/overrides/streaming_reporter.rb +23 -0
  49. data/lib/raven/integrations/railties.rb +1 -0
  50. data/lib/raven/integrations/rake.rb +18 -0
  51. data/lib/raven/integrations/sidekiq.rb +87 -0
  52. data/lib/raven/integrations/tasks.rb +11 -0
  53. data/lib/raven/interface.rb +25 -0
  54. data/lib/raven/interfaces/exception.rb +15 -0
  55. data/lib/raven/interfaces/http.rb +16 -0
  56. data/lib/raven/interfaces/message.rb +20 -0
  57. data/lib/raven/interfaces/single_exception.rb +14 -0
  58. data/lib/raven/interfaces/stack_trace.rb +69 -0
  59. data/lib/raven/linecache.rb +41 -0
  60. data/lib/raven/logger.rb +19 -0
  61. data/lib/raven/processor.rb +15 -0
  62. data/lib/raven/processor/cookies.rb +26 -0
  63. data/lib/raven/processor/http_headers.rb +55 -0
  64. data/lib/raven/processor/post_data.rb +22 -0
  65. data/lib/raven/processor/removecircularreferences.rb +17 -0
  66. data/lib/raven/processor/removestacktrace.rb +24 -0
  67. data/lib/raven/processor/sanitizedata.rb +88 -0
  68. data/lib/raven/processor/utf8conversion.rb +52 -0
  69. data/lib/raven/transports.rb +15 -0
  70. data/lib/raven/transports/dummy.rb +16 -0
  71. data/lib/raven/transports/http.rb +66 -0
  72. data/lib/raven/utils/deep_merge.rb +22 -0
  73. data/lib/raven/utils/real_ip.rb +62 -0
  74. data/lib/raven/version.rb +5 -0
  75. data/lib/sentry-raven-without-integrations.rb +1 -0
  76. data/lib/sentry-raven.rb +1 -0
  77. metadata +141 -0
data/lib/raven/cli.rb ADDED
@@ -0,0 +1,59 @@
1
+ module Raven
2
+ class CLI
3
+ def self.test(dsn = nil, silent = false, config = nil) # rubocop:disable all
4
+ config ||= Raven.configuration
5
+
6
+ config.logger = if silent
7
+ ::Logger.new(nil)
8
+ else
9
+ logger = ::Logger.new(STDOUT)
10
+ logger.formatter = proc do |_severity, _datetime, _progname, msg|
11
+ "-> #{msg}\n"
12
+ end
13
+ logger
14
+ end
15
+
16
+ config.timeout = 5
17
+ config.dsn = dsn if dsn
18
+
19
+ # wipe out env settings to ensure we send the event
20
+ unless config.capture_allowed?
21
+ env_name = config.environments.pop || 'production'
22
+ config.current_environment = env_name
23
+ end
24
+
25
+ instance = Raven::Instance.new(nil, config)
26
+
27
+ instance.logger.debug "Sending a test event:"
28
+ instance.logger.debug ""
29
+
30
+ begin
31
+ 1 / 0
32
+ rescue ZeroDivisionError => exception
33
+ evt = instance.capture_exception(exception)
34
+ end
35
+
36
+ if evt && !(evt.is_a? Thread)
37
+ if evt.is_a? Hash
38
+ instance.logger.debug "-> event ID: #{evt[:event_id]}"
39
+ else
40
+ instance.logger.debug "-> event ID: #{evt.id}"
41
+ end
42
+ elsif evt # async configuration
43
+ if evt.value.is_a? Hash
44
+ instance.logger.debug "-> event ID: #{evt.value[:event_id]}"
45
+ else
46
+ instance.logger.debug "-> event ID: #{evt.value.id}"
47
+ end
48
+ else
49
+ instance.logger.debug ""
50
+ instance.logger.debug "An error occurred while attempting to send the event."
51
+ exit 1
52
+ end
53
+
54
+ instance.logger.debug ""
55
+ instance.logger.debug "Done!"
56
+ evt
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,142 @@
1
+ # frozen_string_literal: true
2
+ require 'base64'
3
+ require 'json'
4
+ require 'zlib'
5
+
6
+ module Raven
7
+ # Encodes events and sends them to the Sentry server.
8
+ class Client
9
+ PROTOCOL_VERSION = '5'.freeze
10
+ USER_AGENT = "raven-ruby/#{Raven::VERSION}".freeze
11
+ CONTENT_TYPE = 'application/json'.freeze
12
+
13
+ attr_accessor :configuration
14
+
15
+ def initialize(configuration)
16
+ @configuration = configuration
17
+ @processors = configuration.processors.map { |v| v.new(self) }
18
+ @state = ClientState.new
19
+ end
20
+
21
+ def send_event(event)
22
+ return false unless configuration.sending_allowed?(event)
23
+
24
+ # Convert to hash
25
+ event = event.to_hash
26
+
27
+ unless @state.should_try?
28
+ failed_send(nil, event)
29
+ return
30
+ end
31
+
32
+ configuration.logger.info "Sending event #{event[:event_id]} to Sentry"
33
+
34
+ content_type, encoded_data = encode(event)
35
+
36
+ begin
37
+ transport.send_event(generate_auth_header, encoded_data,
38
+ :content_type => content_type)
39
+ successful_send
40
+ rescue => e
41
+ failed_send(e, event)
42
+ return
43
+ end
44
+
45
+ event
46
+ end
47
+
48
+ def transport
49
+ @transport ||=
50
+ case configuration.scheme
51
+ when 'http', 'https'
52
+ Transports::HTTP.new(configuration)
53
+ when 'dummy'
54
+ Transports::Dummy.new(configuration)
55
+ else
56
+ fail "Unknown transport scheme '#{configuration.scheme}'"
57
+ end
58
+ end
59
+
60
+ private
61
+
62
+ def encode(event)
63
+ hash = @processors.reduce(event.to_hash) { |a, e| e.process(a) }
64
+ encoded = JSON.fast_generate(hash)
65
+
66
+ case configuration.encoding
67
+ when 'gzip'
68
+ ['application/octet-stream', Base64.strict_encode64(Zlib::Deflate.deflate(encoded))]
69
+ else
70
+ ['application/json', encoded]
71
+ end
72
+ end
73
+
74
+ def get_log_message(event)
75
+ (event && event[:message]) || '<no message value>'
76
+ end
77
+
78
+ def generate_auth_header
79
+ now = Time.now.to_i.to_s
80
+ fields = {
81
+ 'sentry_version' => PROTOCOL_VERSION,
82
+ 'sentry_client' => USER_AGENT,
83
+ 'sentry_timestamp' => now,
84
+ 'sentry_key' => configuration.public_key,
85
+ 'sentry_secret' => configuration.secret_key
86
+ }
87
+ 'Sentry ' + fields.map { |key, value| "#{key}=#{value}" }.join(', ')
88
+ end
89
+
90
+ def successful_send
91
+ @state.success
92
+ end
93
+
94
+ def failed_send(e, event)
95
+ @state.failure
96
+ if e # exception was raised
97
+ configuration.logger.error "Unable to record event with remote Sentry server (#{e.class} - #{e.message}):\n#{e.backtrace[0..10].join("\n")}"
98
+ else
99
+ configuration.logger.error "Not sending event due to previous failure(s)."
100
+ end
101
+ configuration.logger.error("Failed to submit event: #{get_log_message(event)}")
102
+ configuration.transport_failure_callback.call(event) if configuration.transport_failure_callback
103
+ end
104
+ end
105
+
106
+ class ClientState
107
+ def initialize
108
+ reset
109
+ end
110
+
111
+ def should_try?
112
+ return true if @status == :online
113
+
114
+ interval = @retry_after || [@retry_number, 6].min**2
115
+ return true if Time.now - @last_check >= interval
116
+
117
+ false
118
+ end
119
+
120
+ def failure(retry_after = nil)
121
+ @status = :error
122
+ @retry_number += 1
123
+ @last_check = Time.now
124
+ @retry_after = retry_after
125
+ end
126
+
127
+ def success
128
+ reset
129
+ end
130
+
131
+ def reset
132
+ @status = :online
133
+ @retry_number = 0
134
+ @last_check = nil
135
+ @retry_after = nil
136
+ end
137
+
138
+ def failed?
139
+ @status == :error
140
+ end
141
+ end
142
+ end
@@ -0,0 +1,434 @@
1
+ require 'uri'
2
+
3
+ module Raven
4
+ class Configuration
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
9
+
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
14
+
15
+ # Number of lines of code context to capture, or nil for none
16
+ attr_accessor :context_lines
17
+
18
+ # RACK_ENV by default.
19
+ attr_reader :current_environment
20
+
21
+ # Encoding type for event bodies. Must be :json or :gzip.
22
+ attr_reader :encoding
23
+
24
+ # Whitelist of environments that will send notifications to Sentry. Array of Strings.
25
+ attr_accessor :environments
26
+
27
+ # Logger 'progname's to exclude from breadcrumbs
28
+ attr_accessor :exclude_loggers
29
+
30
+ # Array of exception classes that should never be sent. See IGNORE_DEFAULT.
31
+ # You should probably append to this rather than overwrite it.
32
+ attr_accessor :excluded_exceptions
33
+
34
+ # DSN component - set automatically if DSN provided
35
+ attr_accessor :host
36
+
37
+ # The Faraday adapter to be used. Will default to Net::HTTP when not set.
38
+ attr_accessor :http_adapter
39
+
40
+ # A Proc yeilding the faraday builder allowing for further configuration
41
+ # of the faraday adapter
42
+ attr_accessor :faraday_builder
43
+
44
+ # You may provide your own LineCache for matching paths with source files.
45
+ # This may be useful if you need to get source code from places other than
46
+ # the disk. See Raven::LineCache for the required interface you must implement.
47
+ attr_accessor :linecache
48
+
49
+ # Logger used by Raven. In Rails, this is the Rails logger, otherwise
50
+ # Raven provides its own Raven::Logger.
51
+ attr_accessor :logger
52
+
53
+ # Timeout waiting for the Sentry server connection to open in seconds
54
+ attr_accessor :open_timeout
55
+
56
+ # DSN component - set automatically if DSN provided
57
+ attr_accessor :path
58
+
59
+ # DSN component - set automatically if DSN provided
60
+ attr_accessor :port
61
+
62
+ # Processors to run on data before sending upstream. See DEFAULT_PROCESSORS.
63
+ # You should probably append to this rather than overwrite it.
64
+ attr_accessor :processors
65
+
66
+ # Project ID number to send to the Sentry server
67
+ # If you provide a DSN, this will be set automatically.
68
+ attr_accessor :project_id
69
+
70
+ # Project directory root for in_app detection. Could be Rails root, etc.
71
+ # Set automatically for Rails.
72
+ attr_reader :project_root
73
+
74
+ # Proxy information to pass to the HTTP adapter (via Faraday)
75
+ attr_accessor :proxy
76
+
77
+ # Public key for authentication with the Sentry server
78
+ # If you provide a DSN, this will be set automatically.
79
+ attr_accessor :public_key
80
+
81
+ # Turns on ActiveSupport breadcrumbs integration
82
+ attr_accessor :rails_activesupport_breadcrumbs
83
+
84
+ # Rails catches exceptions in the ActionDispatch::ShowExceptions or
85
+ # ActionDispatch::DebugExceptions middlewares, depending on the environment.
86
+ # When `rails_report_rescued_exceptions` is true (it is by default), Raven
87
+ # will report exceptions even when they are rescued by these middlewares.
88
+ attr_accessor :rails_report_rescued_exceptions
89
+
90
+ # Release tag to be passed with every event sent to Sentry.
91
+ # We automatically try to set this to a git SHA or Capistrano release.
92
+ attr_accessor :release
93
+
94
+ # The sampling factor to apply to events. A value of 0.0 will not send
95
+ # any events, and a value of 1.0 will send 100% of events.
96
+ attr_accessor :sample_rate
97
+
98
+ # Boolean - sanitize values that look like credit card numbers
99
+ attr_accessor :sanitize_credit_cards
100
+
101
+ # By default, Sentry censors Hash values when their keys match things like
102
+ # "secret", "password", etc. Provide an array of Strings that, when matched in
103
+ # a hash key, will be censored and not sent to Sentry.
104
+ attr_accessor :sanitize_fields
105
+
106
+ # If you're sure you want to override the default sanitization values, you can
107
+ # add to them to an array of Strings here, e.g. %w(authorization password)
108
+ attr_accessor :sanitize_fields_excluded
109
+
110
+ # Sanitize additional HTTP headers - only Authorization is removed by default.
111
+ attr_accessor :sanitize_http_headers
112
+
113
+ # DSN component - set automatically if DSN provided.
114
+ # Otherwise, can be one of "http", "https", or "dummy"
115
+ attr_accessor :scheme
116
+
117
+ # Secret key for authentication with the Sentry server
118
+ # If you provide a DSN, this will be set automatically.
119
+ attr_accessor :secret_key
120
+
121
+ # Include module versions in reports - boolean.
122
+ attr_accessor :send_modules
123
+
124
+ # Simple server string - set this to the DSN found on your Sentry settings.
125
+ attr_reader :server
126
+
127
+ attr_accessor :server_name
128
+
129
+ # Provide a configurable callback to determine event capture.
130
+ # Note that the object passed into the block will be a String (messages) or
131
+ # an exception.
132
+ # e.g. lambda { |exc_or_msg| exc_or_msg.some_attr == false }
133
+ attr_reader :should_capture
134
+
135
+ # Silences ready message when true.
136
+ attr_accessor :silence_ready
137
+
138
+ # SSL settings passed directly to Faraday's ssl option
139
+ attr_accessor :ssl
140
+
141
+ # The path to the SSL certificate file
142
+ attr_accessor :ssl_ca_file
143
+
144
+ # Should the SSL certificate of the server be verified?
145
+ attr_accessor :ssl_verification
146
+
147
+ # Default tags for events. Hash.
148
+ attr_accessor :tags
149
+
150
+ # Timeout when waiting for the server to return data in seconds.
151
+ attr_accessor :timeout
152
+
153
+ # Optional Proc, called when the Sentry server cannot be contacted for any reason
154
+ # E.g. lambda { |event| Thread.new { MyJobProcessor.send_email(event) } }
155
+ attr_reader :transport_failure_callback
156
+
157
+ # Errors object - an Array that contains error messages. See #
158
+ attr_reader :errors
159
+
160
+ IGNORE_DEFAULT = [
161
+ 'AbstractController::ActionNotFound',
162
+ 'ActionController::InvalidAuthenticityToken',
163
+ 'ActionController::RoutingError',
164
+ 'ActionController::UnknownAction',
165
+ 'ActiveRecord::RecordNotFound',
166
+ 'CGI::Session::CookieStore::TamperedWithCookie',
167
+ 'Mongoid::Errors::DocumentNotFound',
168
+ 'Sinatra::NotFound',
169
+ 'ActiveJob::DeserializationError'
170
+ ].freeze
171
+
172
+ # Note the order - we have to remove circular references and bad characters
173
+ # before passing to other processors.
174
+ DEFAULT_PROCESSORS = [
175
+ Raven::Processor::RemoveCircularReferences,
176
+ Raven::Processor::UTF8Conversion,
177
+ Raven::Processor::SanitizeData,
178
+ Raven::Processor::Cookies,
179
+ Raven::Processor::PostData,
180
+ Raven::Processor::HTTPHeaders
181
+ ].freeze
182
+
183
+ LOG_PREFIX = "** [Raven] ".freeze
184
+ MODULE_SEPARATOR = "::".freeze
185
+
186
+ def initialize
187
+ self.async = false
188
+ self.context_lines = 3
189
+ self.current_environment = current_environment_from_env
190
+ self.encoding = 'gzip'
191
+ self.environments = []
192
+ self.exclude_loggers = []
193
+ self.excluded_exceptions = IGNORE_DEFAULT.dup
194
+ self.linecache = ::Raven::LineCache.new
195
+ self.logger = ::Raven::Logger.new(STDOUT)
196
+ self.open_timeout = 1
197
+ self.processors = DEFAULT_PROCESSORS.dup
198
+ self.project_root = detect_project_root
199
+ self.rails_activesupport_breadcrumbs = false
200
+ self.rails_report_rescued_exceptions = true
201
+ self.release = detect_release
202
+ self.sample_rate = 1.0
203
+ self.sanitize_credit_cards = true
204
+ self.sanitize_fields = []
205
+ self.sanitize_fields_excluded = []
206
+ self.sanitize_http_headers = []
207
+ self.send_modules = true
208
+ self.server = ENV['SENTRY_DSN']
209
+ self.server_name = server_name_from_env
210
+ self.should_capture = false
211
+ self.ssl_verification = true
212
+ self.tags = {}
213
+ self.timeout = 2
214
+ self.transport_failure_callback = false
215
+ end
216
+
217
+ def server=(value)
218
+ return if value.nil?
219
+ uri = URI.parse(value)
220
+ uri_path = uri.path.split('/')
221
+
222
+ if uri.user
223
+ # DSN-style string
224
+ self.project_id = uri_path.pop
225
+ self.public_key = uri.user
226
+ self.secret_key = uri.password
227
+ end
228
+
229
+ self.scheme = uri.scheme
230
+ self.host = uri.host
231
+ self.port = uri.port if uri.port
232
+ self.path = uri_path.join('/')
233
+
234
+ # For anyone who wants to read the base server string
235
+ @server = "#{scheme}://#{host}"
236
+ @server << ":#{port}" unless port == { 'http' => 80, 'https' => 443 }[scheme]
237
+ @server << path
238
+ end
239
+ alias dsn= server=
240
+
241
+ def encoding=(encoding)
242
+ raise(Error, 'Unsupported encoding') unless %w(gzip json).include? encoding
243
+ @encoding = encoding
244
+ end
245
+
246
+ def async=(value)
247
+ unless value == false || value.respond_to?(:call)
248
+ raise(ArgumentError, "async must be callable (or false to disable)")
249
+ end
250
+ @async = value
251
+ end
252
+
253
+ def transport_failure_callback=(value)
254
+ unless value == false || value.respond_to?(:call)
255
+ raise(ArgumentError, "transport_failure_callback must be callable (or false to disable)")
256
+ end
257
+ @transport_failure_callback = value
258
+ end
259
+
260
+ def should_capture=(value)
261
+ unless value == false || value.respond_to?(:call)
262
+ raise ArgumentError, "should_capture must be callable (or false to disable)"
263
+ end
264
+ @should_capture = value
265
+ end
266
+
267
+ # Allows config options to be read like a hash
268
+ #
269
+ # @param [Symbol] option Key for a given attribute
270
+ def [](option)
271
+ public_send(option)
272
+ end
273
+
274
+ def current_environment=(environment)
275
+ @current_environment = environment.to_s
276
+ end
277
+
278
+ def capture_allowed?(message_or_exc = nil)
279
+ @errors = []
280
+
281
+ valid? &&
282
+ capture_in_current_environment? &&
283
+ capture_allowed_by_callback?(message_or_exc) &&
284
+ sample_allowed?
285
+ end
286
+ # If we cannot capture, we cannot send.
287
+ alias sending_allowed? capture_allowed?
288
+
289
+ def error_messages
290
+ @errors = [errors[0]] + errors[1..-1].map(&:downcase) # fix case of all but first
291
+ errors.join(", ")
292
+ end
293
+
294
+ def project_root=(root_dir)
295
+ @project_root = root_dir
296
+ Backtrace::Line.instance_variable_set(:@in_app_pattern, nil) # blow away cache
297
+ end
298
+
299
+ def exception_class_allowed?(exc)
300
+ if exc.is_a?(Raven::Error)
301
+ # Try to prevent error reporting loops
302
+ logger.debug "Refusing to capture Raven error: #{exc.inspect}"
303
+ false
304
+ elsif excluded_exception?(exc)
305
+ logger.debug "User excluded error: #{exc.inspect}"
306
+ false
307
+ else
308
+ true
309
+ end
310
+ end
311
+
312
+ private
313
+
314
+ def detect_project_root
315
+ if defined? Rails.root # we are in a Rails application
316
+ Rails.root.to_s
317
+ else
318
+ Dir.pwd
319
+ end
320
+ end
321
+
322
+ def detect_release
323
+ detect_release_from_git ||
324
+ detect_release_from_capistrano ||
325
+ detect_release_from_heroku
326
+ rescue => ex
327
+ logger.error "Error detecting release: #{ex.message}"
328
+ end
329
+
330
+ def excluded_exception?(exc)
331
+ excluded_exceptions.any? { |x| get_exception_class(x) === exc }
332
+ end
333
+
334
+ def get_exception_class(x)
335
+ x.is_a?(Module) ? x : qualified_const_get(x)
336
+ end
337
+
338
+ # In Ruby <2.0 const_get can't lookup "SomeModule::SomeClass" in one go
339
+ def qualified_const_get(x)
340
+ x = x.to_s
341
+ if !x.match(/::/)
342
+ Object.const_get(x)
343
+ else
344
+ x.split(MODULE_SEPARATOR).reject(&:empty?).inject(Object) { |a, e| a.const_get(e) }
345
+ end
346
+ rescue NameError # There's no way to safely ask if a constant exist for an unknown string
347
+ nil
348
+ end
349
+
350
+ def detect_release_from_heroku
351
+ return unless running_on_heroku?
352
+ logger.warn(heroku_dyno_metadata_message) && return unless ENV['HEROKU_SLUG_COMMIT']
353
+
354
+ ENV['HEROKU_SLUG_COMMIT']
355
+ end
356
+
357
+ def running_on_heroku?
358
+ File.directory?("/etc/heroku")
359
+ end
360
+
361
+ def heroku_dyno_metadata_message
362
+ "You are running on Heroku but haven't enabled Dyno Metadata. For Sentry's "\
363
+ "release detection to work correctly, please run `heroku labs:enable runtime-dyno-metadata`"
364
+ end
365
+
366
+ def detect_release_from_capistrano
367
+ revision_file = File.join(project_root, 'REVISION')
368
+ revision_log = File.join(project_root, '..', 'revisions.log')
369
+
370
+ if File.exist?(revision_file)
371
+ File.read(revision_file).strip
372
+ elsif File.exist?(revision_log)
373
+ File.open(revision_log).to_a.last.strip.sub(/.*as release ([0-9]+).*/, '\1')
374
+ end
375
+ end
376
+
377
+ def detect_release_from_git
378
+ Raven.sys_command("git rev-parse --short HEAD") if File.directory?(".git")
379
+ end
380
+
381
+ def capture_in_current_environment?
382
+ return true unless environments.any? && !environments.include?(current_environment)
383
+ @errors << "Not configured to send/capture in environment '#{current_environment}'"
384
+ false
385
+ end
386
+
387
+ def capture_allowed_by_callback?(message_or_exc)
388
+ return true if !should_capture || message_or_exc.nil? || should_capture.call(*[message_or_exc])
389
+ @errors << "should_capture returned false"
390
+ false
391
+ end
392
+
393
+ def valid?
394
+ return true if %w(server host path public_key secret_key project_id).all? { |k| public_send(k) }
395
+ if server
396
+ %w(server host path public_key secret_key project_id).map do |key|
397
+ @errors << "No #{key} specified" unless public_send(key)
398
+ end
399
+ else
400
+ @errors << "DSN not set"
401
+ end
402
+ false
403
+ end
404
+
405
+ def sample_allowed?
406
+ return true if sample_rate == 1.0
407
+ if Random::DEFAULT.rand >= sample_rate
408
+ @errors << "Excluded by random sample"
409
+ false
410
+ else
411
+ true
412
+ end
413
+ end
414
+
415
+ # Try to resolve the hostname to an FQDN, but fall back to whatever
416
+ # the load name is.
417
+ def resolve_hostname
418
+ Socket.gethostname ||
419
+ Socket.gethostbyname(hostname).first rescue server_name
420
+ end
421
+
422
+ def current_environment_from_env
423
+ ENV['SENTRY_CURRENT_ENV'] || ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'default'
424
+ end
425
+
426
+ def server_name_from_env
427
+ if running_on_heroku?
428
+ ENV['DYNO']
429
+ else
430
+ resolve_hostname
431
+ end
432
+ end
433
+ end
434
+ end