sentry-raven 2.1.4 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1d53252f2dcacc37eaa626b7b91c244349ae8032
4
- data.tar.gz: 16db0fcdac70b75df576a2296cefb9abb29cf661
3
+ metadata.gz: 7d602b648b5c1c87ce074dc68be686aeca0b6752
4
+ data.tar.gz: c6c6219649f4e8ab1f2c015f3ce6eac40ee51a39
5
5
  SHA512:
6
- metadata.gz: b1a5d740487dca1c2b6c4a0e67d0c4d2e6292a4a7d32fe3751ca7413e4819c576ada5b0111acf7112f917c682f0e9d718b8b9e941adc7d24500b4f37bedf30ad
7
- data.tar.gz: 8c8dc7a2c659c85ec8f00f2becf1f744312f775891cad58058114631af2374c2c95c45aa8bead8d0c14aee30549b0842d3c579ed322231ec0db8b61cf91766d6
6
+ metadata.gz: eed297ddb38d1321c24c5abde706eb899d185de9fb3d3650679201e4cabe45a6594623ef9117e3df9c68ad857952d1a23701f66ab7af6ced344718a6cf798dcc
7
+ data.tar.gz: 1143923e646e592a227a86fdbe962d93abe366c381bda6b23dff9dbd5b1b1cfef42ef8c00e4682babdaa3752e8338c9cae2960dd6d7ee6718eae9e9f3c89fde4
data/README.md CHANGED
@@ -7,7 +7,7 @@ A client and integration layer for the [Sentry](https://github.com/getsentry/sen
7
7
 
8
8
  ## Requirements
9
9
 
10
- We test on Ruby 1.9, 2.2 and 2.3 at the latest patchlevel/teeny version. Our Rails integration works with Rails 4.2+. JRuby support is experimental - check TravisCI to see if the build is passing or failing.
10
+ We test on Ruby 1.9, 2.2 and 2.3 at the latest patchlevel/teeny version. We also support JRuby 1.7 and 9.0. Our Rails integration works with Rails 4.2+.
11
11
 
12
12
  ## Getting Started
13
13
 
@@ -57,6 +57,89 @@ rescue ZeroDivisionError => exception
57
57
  end
58
58
  ```
59
59
 
60
+ ### More configuration
61
+
62
+ You're all set - but there's a few more settings you may want to know about too!
63
+
64
+ #### DSN
65
+
66
+ While we advise that you set your Sentry DSN through the `SENTRY_DSN` environment
67
+ variable, there are two other configuration settings for controlling Raven:
68
+
69
+ ```ruby
70
+ # DSN can be configured as a config setting instead.
71
+ # Place in config/initializers or similar.
72
+ Raven.configure do |config|
73
+ config.dsn = 'your_dsn'
74
+ end
75
+ ```
76
+
77
+ And, while not necessary if using `SENTRY_DSN`, you can also provide an `environments`
78
+ setting. Raven will only capture events when `RACK_ENV` or `RAILS_ENV` matches
79
+ an environment in the list.
80
+
81
+ ```ruby
82
+ Raven.configure do |config|
83
+ config.environments = %w[staging production]
84
+ end
85
+ ```
86
+
87
+ #### async
88
+
89
+ When an error or message occurs, the notification is immediately sent to Sentry. Raven can be configured to send asynchronously:
90
+
91
+ ```ruby
92
+ config.async = lambda { |event|
93
+ Thread.new { Raven.send_event(event) }
94
+ }
95
+ ```
96
+
97
+ Using a thread to send events will be adequate for truly parallel Ruby platforms such as JRuby, though the benefit on MRI/CRuby will be limited. If the async callback raises an exception, Raven will attempt to send synchronously.
98
+
99
+ We recommend creating a background job, using your background job processor, that will send Sentry notifications in the background. Rather than enqueuing an entire Raven::Event object, we recommend providing the Hash representation of an event as a job argument. Here’s an example for ActiveJob:
100
+
101
+ ```ruby
102
+ config.async = lambda { |event|
103
+ SentryJob.perform_later(event.to_hash)
104
+ }
105
+ class SentryJob < ActiveJob::Base
106
+ queue_as :default
107
+
108
+ def perform(event)
109
+ Raven.send_event(event)
110
+ end
111
+ end
112
+ ```
113
+
114
+ #### transport_failure_callback
115
+
116
+ If Raven fails to send an event to Sentry for any reason (either the Sentry server has returned a 4XX or 5XX response), this Proc or lambda will be called.
117
+
118
+ ```ruby
119
+ config.transport_failure_callback = lambda { |event|
120
+ AdminMailer.email_admins("Oh god, it's on fire!", event).deliver_later
121
+ }
122
+ ```
123
+
124
+ #### Context
125
+
126
+ Much of the usefulness of Sentry comes from additional context data with the events. Raven makes this very convenient by providing methods to set thread local context data that is then submitted automatically with all events.
127
+
128
+ There are three primary methods for providing request context:
129
+
130
+ ```ruby
131
+ # bind the logged in user
132
+ Raven.user_context email: 'foo@example.com'
133
+
134
+ # tag the request with something interesting
135
+ Raven.tags_context interesting: 'yes'
136
+
137
+ # provide a bit of additional context
138
+ Raven.extra_context happiness: 'very'
139
+ ```
140
+
141
+ For more information, see [Context](https://docs.sentry.io/clients/ruby/context/).
142
+
60
143
  ## More Information
61
144
 
62
145
  * [Documentation](https://docs.getsentry.com/hosted/clients/ruby/)
data/lib/raven/base.rb CHANGED
@@ -92,8 +92,9 @@ module Raven
92
92
  end
93
93
 
94
94
  def sys_command(command)
95
- result = `#{command}` rescue nil
96
- result.strip if result != "" && result
95
+ result = `#{command} 2>&1` rescue nil
96
+ return if result.nil? || result.empty? || $CHILD_STATUS.exitstatus != 0
97
+ result.strip
97
98
  end
98
99
  end
99
100
  end
data/lib/raven/cli.rb CHANGED
@@ -23,8 +23,6 @@ module Raven
23
23
  Raven.configuration.current_environment = env_name
24
24
  end
25
25
 
26
- Raven.configuration.verify!
27
-
28
26
  Raven.logger.debug "Sending a test event:"
29
27
  Raven.logger.debug ""
30
28
 
data/lib/raven/client.rb CHANGED
@@ -32,7 +32,7 @@ module Raven
32
32
  return
33
33
  end
34
34
 
35
- Raven.logger.debug "Sending event #{event[:event_id]} to Sentry"
35
+ configuration.logger.debug "Sending event #{event[:event_id]} to Sentry"
36
36
 
37
37
  content_type, encoded_data = encode(event)
38
38
 
@@ -97,12 +97,12 @@ module Raven
97
97
  def failed_send(e, event)
98
98
  @state.failure
99
99
  if e # exception was raised
100
- Raven.logger.error "Unable to record event with remote Sentry server (#{e.class} - #{e.message})"
101
- e.backtrace[0..10].each { |line| Raven.logger.error(line) }
100
+ configuration.logger.error "Unable to record event with remote Sentry server (#{e.class} - #{e.message})"
101
+ e.backtrace[0..10].each { |line| configuration.logger.error(line) }
102
102
  else
103
- Raven.logger.error "Not sending event due to previous failure(s)."
103
+ configuration.logger.error "Not sending event due to previous failure(s)."
104
104
  end
105
- Raven.logger.error("Failed to submit event: #{get_log_message(event)}")
105
+ configuration.logger.error("Failed to submit event: #{get_log_message(event)}")
106
106
  configuration.transport_failure_callback.call(event) if configuration.transport_failure_callback
107
107
  end
108
108
  end
@@ -38,6 +38,11 @@ module Raven
38
38
  # The Faraday adapter to be used. Will default to Net::HTTP when not set.
39
39
  attr_accessor :http_adapter
40
40
 
41
+ # You may provide your own LineCache for matching paths with source files.
42
+ # This may be useful if you need to get source code from places other than
43
+ # the disk. See Raven::LineCache for the required interface you must implement.
44
+ attr_accessor :linecache
45
+
41
46
  # Logger used by Raven. In Rails, this is the Rails logger, otherwise
42
47
  # Raven provides its own Raven::Logger.
43
48
  attr_accessor :logger
@@ -138,6 +143,9 @@ module Raven
138
143
  # E.g. lambda { |event| Thread.new { MyJobProcessor.send_email(event) } }
139
144
  attr_reader :transport_failure_callback
140
145
 
146
+ # Errors object - an Array that contains error messages. See #
147
+ attr_reader :errors
148
+
141
149
  IGNORE_DEFAULT = [
142
150
  'AbstractController::ActionNotFound',
143
151
  'ActionController::InvalidAuthenticityToken',
@@ -160,6 +168,8 @@ module Raven
160
168
  Raven::Processor::HTTPHeaders
161
169
  ].freeze
162
170
 
171
+ LOG_PREFIX = "** [Raven] ".freeze
172
+
163
173
  def initialize
164
174
  self.async = false
165
175
  self.context_lines = 3
@@ -168,6 +178,8 @@ module Raven
168
178
  self.environments = []
169
179
  self.exclude_loggers = []
170
180
  self.excluded_exceptions = IGNORE_DEFAULT.dup
181
+ self.linecache = ::Raven::LineCache.new
182
+ self.logger = ::Raven::Logger.new(STDOUT)
171
183
  self.open_timeout = 1
172
184
  self.processors = DEFAULT_PROCESSORS.dup
173
185
  self.proxy = nil
@@ -248,17 +260,21 @@ module Raven
248
260
  end
249
261
 
250
262
  def capture_allowed?(message_or_exc = nil)
251
- capture_in_current_environment? &&
252
- capture_allowed_by_callback?(message_or_exc)
263
+ @errors = []
264
+ @errors = valid? +
265
+ capture_in_current_environment? +
266
+ capture_allowed_by_callback?(message_or_exc)
267
+ if @errors.any?
268
+ false
269
+ else
270
+ true
271
+ end
253
272
  end
254
-
255
273
  # If we cannot capture, we cannot send.
256
274
  alias sending_allowed? capture_allowed?
257
275
 
258
- def verify!
259
- %w(server public_key secret_key project_id).each do |key|
260
- raise(Error, "No #{key} specified") unless public_send key
261
- end
276
+ def error_messages
277
+ errors.join(", ")
262
278
  end
263
279
 
264
280
  def project_root=(root_dir)
@@ -283,7 +299,7 @@ module Raven
283
299
  hash = JSON.parse(sys_dyno_info)
284
300
  hash && hash["release"] && hash["release"]["commit"]
285
301
  rescue JSON::JSONError
286
- Raven.logger.error "Cannot parse Heroku JSON: #{sys_dyno_info}"
302
+ logger.error "Cannot parse Heroku JSON: #{sys_dyno_info}"
287
303
  end
288
304
  end
289
305
 
@@ -299,12 +315,23 @@ module Raven
299
315
  end
300
316
 
301
317
  def capture_in_current_environment?
302
- !!server && (environments.empty? || environments.include?(current_environment))
318
+ if environments.any? && !environments.include?(current_environment)
319
+ ["Not configured to send/capture in environment '#{current_environment}'"]
320
+ else
321
+ []
322
+ end
303
323
  end
304
324
 
305
325
  def capture_allowed_by_callback?(message_or_exc)
306
- return true if !should_capture || message_or_exc.nil?
307
- should_capture.call(*[message_or_exc])
326
+ return [] if !should_capture || message_or_exc.nil? || should_capture.call(*[message_or_exc])
327
+ ["should_capture returned false"]
328
+ end
329
+
330
+ def valid?
331
+ err = %w(server host path public_key secret_key project_id).map do |key|
332
+ "No #{key} specified" unless public_send(key)
333
+ end
334
+ err.compact
308
335
  end
309
336
 
310
337
  # Try to resolve the hostname to an FQDN, but fall back to whatever
data/lib/raven/event.rb CHANGED
@@ -26,13 +26,14 @@ module Raven
26
26
  attr_accessor :id, :timestamp, :time_spent, :level, :logger,
27
27
  :culprit, :server_name, :release, :modules, :extra, :tags,
28
28
  :context, :configuration, :checksum, :fingerprint, :environment,
29
- :server_os, :runtime, :breadcrumbs, :user, :backtrace
29
+ :server_os, :runtime, :breadcrumbs, :user, :backtrace, :linecache
30
30
 
31
31
  def initialize(init = {})
32
- @configuration = Raven.configuration
32
+ @configuration = init[:configuration] || Raven.configuration
33
33
  @interfaces = {}
34
- @breadcrumbs = Raven.breadcrumbs
35
- @context = Raven.context
34
+ @breadcrumbs = init[:breadcrumbs] || Raven.breadcrumbs
35
+ @context = init[:context] || Raven.context
36
+ @linecache = @configuration.linecache
36
37
  @id = SecureRandom.uuid.delete("-")
37
38
  @timestamp = Time.now.utc
38
39
  @time_spent = nil
@@ -95,11 +96,11 @@ module Raven
95
96
  configuration = options[:configuration] || Raven.configuration
96
97
  if exc.is_a?(Raven::Error)
97
98
  # Try to prevent error reporting loops
98
- Raven.logger.info "Refusing to capture Raven error: #{exc.inspect}"
99
+ configuration.logger.info "Refusing to capture Raven error: #{exc.inspect}"
99
100
  return nil
100
101
  end
101
102
  if configuration[:excluded_exceptions].any? { |x| get_exception_class(x) === exc }
102
- Raven.logger.info "User excluded error: #{exc.inspect}"
103
+ configuration.logger.info "User excluded error: #{exc.inspect}"
103
104
  return nil
104
105
  end
105
106
 
@@ -213,10 +214,6 @@ module Raven
213
214
 
214
215
  evt.culprit = evt.get_culprit(int.frames)
215
216
  end
216
-
217
- # Because linecache can go to hell
218
- def _source_lines(_path, _from, _to)
219
- end
220
217
  end
221
218
 
222
219
  def list_gem_specs
@@ -270,11 +267,7 @@ module Raven
270
267
  end
271
268
 
272
269
  def get_file_context(filename, lineno, context)
273
- return nil, nil, nil unless Raven::LineCache.valid_file?(filename)
274
- lines = Array.new(2 * context + 1) do |i|
275
- Raven::LineCache.getline(filename, lineno - context + i)
276
- end
277
- [lines[0..(context - 1)], lines[context], lines[(context + 1)..-1]]
270
+ linecache.get_file_context(filename, lineno, context)
278
271
  end
279
272
 
280
273
  def get_culprit(frames)
@@ -19,16 +19,15 @@ module Raven
19
19
  # end
20
20
  # end
21
21
  class Instance
22
- # The client object is responsible for delivering formatted data to the
23
- # Sentry server. Must respond to #send. See Raven::Client.
22
+ # See Raven::Client.
24
23
  attr_writer :client
25
24
 
26
- # A Raven configuration object. Must act like a hash and return sensible
27
- # values for all Raven configuration options. See Raven::Configuration.
28
- attr_writer :configuration
25
+ # See Raven::Configuration.
26
+ attr_accessor :configuration
29
27
 
30
- def initialize(context = nil)
28
+ def initialize(context = nil, config = nil)
31
29
  @context = @explicit_context = context
30
+ self.configuration = config || Configuration.new
32
31
  end
33
32
 
34
33
  def context
@@ -40,13 +39,7 @@ module Raven
40
39
  end
41
40
 
42
41
  def logger
43
- @logger ||= Logger.new
44
- end
45
-
46
- # The configuration object.
47
- # @see Raven.configure
48
- def configuration
49
- @configuration ||= Configuration.new
42
+ configuration.logger
50
43
  end
51
44
 
52
45
  # The client object is responsible for delivering formatted data to the
@@ -61,7 +54,7 @@ module Raven
61
54
  if configuration.capture_allowed?
62
55
  logger.info "Raven #{VERSION} ready to catch errors"
63
56
  else
64
- logger.info "Raven #{VERSION} configured not to capture errors."
57
+ logger.info "Raven #{VERSION} configured not to capture errors: #{configuration.error_messages}"
65
58
  end
66
59
  end
67
60
 
@@ -111,11 +104,13 @@ module Raven
111
104
 
112
105
  def capture_type(obj, options = {})
113
106
  unless configuration.capture_allowed?(obj)
114
- Raven.logger.debug("#{obj} excluded from capture due to environment or should_capture callback")
107
+ logger.debug("#{obj} excluded from capture: #{configuration.error_messages}")
115
108
  return false
116
109
  end
117
110
 
118
111
  message_or_exc = obj.is_a?(String) ? "message" : "exception"
112
+ options[:configuration] = configuration
113
+ options[:context] = context
119
114
  if (evt = Event.send("from_" + message_or_exc, obj, options))
120
115
  yield evt if block_given?
121
116
  if configuration.async?
@@ -124,7 +119,7 @@ module Raven
124
119
  # processors (esp ActiveJob) may not like weird types in the event hash
125
120
  configuration.async.call(evt.to_json_compatible)
126
121
  rescue => ex
127
- Raven.logger.error("async event sending failed: #{ex.message}")
122
+ logger.error("async event sending failed: #{ex.message}")
128
123
  send_event(evt)
129
124
  end
130
125
  else
@@ -135,6 +130,9 @@ module Raven
135
130
  end
136
131
  end
137
132
 
133
+ alias capture_message capture_type
134
+ alias capture_exception capture_type
135
+
138
136
  def last_event_id
139
137
  Thread.current["sentry_#{object_id}_last_event_id".to_sym]
140
138
  end
@@ -1,4 +1,3 @@
1
- # rubocop:disable Style/FileName
2
1
  # We need to do this because of the way integration loading works
3
2
  require "rack/timeout/base"
4
3
 
@@ -1,27 +1,41 @@
1
1
  module Raven
2
2
  class LineCache
3
- class << self
4
- CACHE = {} # rubocop:disable Style/MutableConstant
3
+ def initialize
4
+ @cache = {}
5
+ end
5
6
 
6
- def valid_file?(path)
7
- lines = getlines(path)
8
- !lines.nil?
7
+ # Any linecache you provide to Raven must implement this method.
8
+ # Returns an Array of Strings representing the lines in the source
9
+ # file. The number of lines retrieved is (2 * context) + 1, the middle
10
+ # line should be the line requested by lineno. See specs for more information.
11
+ def get_file_context(filename, lineno, context)
12
+ return nil, nil, nil unless valid_path?(filename)
13
+ lines = Array.new(2 * context + 1) do |i|
14
+ getline(filename, lineno - context + i)
9
15
  end
16
+ [lines[0..(context - 1)], lines[context], lines[(context + 1)..-1]]
17
+ end
10
18
 
11
- def getlines(path)
12
- CACHE[path] ||= begin
13
- IO.readlines(path)
14
- rescue
15
- nil
16
- end
17
- end
19
+ private
18
20
 
19
- def getline(path, n)
20
- return nil if n < 1
21
- lines = getlines(path)
22
- return nil if lines.nil?
23
- lines[n - 1]
21
+ def valid_path?(path)
22
+ lines = getlines(path)
23
+ !lines.nil?
24
+ end
25
+
26
+ def getlines(path)
27
+ @cache[path] ||= begin
28
+ IO.readlines(path)
29
+ rescue
30
+ nil
24
31
  end
25
32
  end
33
+
34
+ def getline(path, n)
35
+ return nil if n < 1
36
+ lines = getlines(path)
37
+ return nil if lines.nil?
38
+ lines[n - 1]
39
+ end
26
40
  end
27
41
  end
data/lib/raven/logger.rb CHANGED
@@ -2,25 +2,16 @@
2
2
  require 'logger'
3
3
 
4
4
  module Raven
5
- class Logger
5
+ class Logger < ::Logger
6
6
  LOG_PREFIX = "** [Raven] ".freeze
7
+ PROGNAME = "sentry".freeze
7
8
 
8
- [
9
- :fatal,
10
- :error,
11
- :warn,
12
- :info,
13
- :debug
14
- ].each do |level|
15
- define_method level do |*args, &block|
16
- logger = Raven.configuration[:logger]
17
- logger = ::Logger.new(STDOUT) if logger.nil?
18
- return unless logger
19
-
20
- msg = args[0] # Block-level default args is a 1.9 feature
21
- msg ||= block.call if block
22
-
23
- logger.send(level, "sentry") { "#{LOG_PREFIX}#{msg}" }
9
+ def initialize(*)
10
+ super
11
+ original_formatter = ::Logger::Formatter.new
12
+ @default_formatter = proc do |severity, datetime, _progname, msg|
13
+ msg = "#{LOG_PREFIX}#{msg}"
14
+ original_formatter.call(severity, datetime, PROGNAME, msg)
24
15
  end
25
16
  end
26
17
  end
@@ -15,6 +15,7 @@ module Raven
15
15
  end
16
16
 
17
17
  def process(value)
18
+ return value if value.frozen?
18
19
  value.each_with_object(value) { |(k, v), memo| memo[k] = sanitize(k, v) }
19
20
  end
20
21
 
@@ -12,12 +12,6 @@ module Raven
12
12
  def send_event # (auth_header, data, options = {})
13
13
  raise NotImplementedError, 'Abstract method not implemented'
14
14
  end
15
-
16
- protected
17
-
18
- def verify_configuration
19
- configuration.verify!
20
- end
21
15
  end
22
16
  end
23
17
  end
@@ -15,6 +15,10 @@ module Raven
15
15
  end
16
16
 
17
17
  def send_event(auth_header, data, options = {})
18
+ unless configuration.sending_allowed?
19
+ logger.debug("Event not sent: #{configuration.error_messages}")
20
+ end
21
+
18
22
  project_id = configuration[:project_id]
19
23
  path = configuration[:path] + "/"
20
24
 
@@ -24,15 +28,15 @@ module Raven
24
28
  req.body = data
25
29
  end
26
30
  rescue Faraday::ClientError => ex
27
- raise Raven::Error, ex.message
31
+ error_info = ex.message
32
+ error_info += " Error in headers is: #{ex.response[:headers]['x-sentry-error']}" if ex.response[:headers]['x-sentry-error']
33
+ raise Raven::Error, error_info
28
34
  end
29
35
 
30
36
  private
31
37
 
32
38
  def set_conn
33
- verify_configuration
34
-
35
- Raven.logger.debug "Raven HTTP Transport connecting to #{configuration.server}"
39
+ configuration.logger.debug "Raven HTTP Transport connecting to #{configuration.server}"
36
40
 
37
41
  ssl_configuration = configuration.ssl || {}
38
42
  ssl_configuration[:verify] = configuration.ssl_verification
@@ -1,3 +1,4 @@
1
+ # rubocop:disable all
1
2
  module Raven
2
3
  module Utils
3
4
  # ported from ActiveSupport
@@ -26,3 +27,4 @@ module Raven
26
27
  end
27
28
  end
28
29
  end
30
+ # rubocop:enable all
data/lib/raven/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
  module Raven
3
3
  # Freezing this constant breaks in 1.9.x
4
- VERSION = "2.1.4" # rubocop:disable Style/MutableConstant
4
+ VERSION = "2.2.0" # rubocop:disable Style/MutableConstant
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sentry-raven
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.4
4
+ version: 2.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sentry Team
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-11-18 00:00:00.000000000 Z
11
+ date: 2016-12-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -19,7 +19,7 @@ dependencies:
19
19
  version: 0.7.6
20
20
  - - "<"
21
21
  - !ruby/object:Gem::Version
22
- version: 0.10.x
22
+ version: '1.0'
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
@@ -29,7 +29,7 @@ dependencies:
29
29
  version: 0.7.6
30
30
  - - "<"
31
31
  - !ruby/object:Gem::Version
32
- version: 0.10.x
32
+ version: '1.0'
33
33
  - !ruby/object:Gem::Dependency
34
34
  name: rake
35
35
  requirement: !ruby/object:Gem::Requirement