sentry-raven 1.1.0 → 1.2.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 35bfb0236a3a8236cfe5c2ad37592f973c9612be
4
- data.tar.gz: 79ee56bfc0b8243f4dd50111aa253498950548a5
3
+ metadata.gz: bd0a24e20bb50a520df079f36059cb0adb5d7673
4
+ data.tar.gz: c89f6a2a4146c6ac291e527e7ec362f12a2e82eb
5
5
  SHA512:
6
- metadata.gz: d3caa9d91239b8f4bb0355f5a0bf6ea5af48e616e91f43a7abdc62f91f65638f0fca6d73dfeb63be6ba610b40a4c2defa23ca40e2256c1a8f5ad5dfd1ea5d137
7
- data.tar.gz: 1cbeccc7169fba6eef992a6eb11d5c6bd8ec6aef89a780c200cb10f011a4bf970109aac68c870975f44a80849e428316382969d92ea70b300278ebe3485cf310
6
+ metadata.gz: ddb34f4ff56c51af45e4c369f61f9a2a01e24107bc97eca694abbb1a501902cac5254fc912e1eb9e794b8d6c21beff0a49570bb1b114d748676b39cfb7862735
7
+ data.tar.gz: b35f1609019436dd7f42ef6ed976f53bde10a15eab61b5c6a1bc362b99fa571e459913cafb02da7e1281ff366c70ba1a6a06ff8abf326fd455d1824f4a7cfe00
@@ -1,5 +1,6 @@
1
1
  require 'raven/version'
2
2
  require 'raven/backtrace'
3
+ require 'raven/breadcrumbs'
3
4
  require 'raven/processor'
4
5
  require 'raven/processor/sanitizedata'
5
6
  require 'raven/processor/removecircularreferences'
@@ -17,187 +18,33 @@ require 'raven/interfaces/single_exception'
17
18
  require 'raven/interfaces/stack_trace'
18
19
  require 'raven/interfaces/http'
19
20
  require 'raven/utils/deep_merge'
21
+ require 'raven/instance'
22
+
23
+ require 'forwardable'
20
24
 
21
25
  module Raven
22
26
  AVAILABLE_INTEGRATIONS = %w[delayed_job railties sidekiq rack rake].freeze
23
27
 
24
28
  class << self
25
- # The client object is responsible for delivering formatted data to the Sentry server.
26
- # Must respond to #send. See Raven::Client.
27
- attr_writer :client
28
-
29
- # A Raven configuration object. Must act like a hash and return sensible
30
- # values for all Raven configuration options. See Raven::Configuration.
31
- attr_writer :configuration
32
-
33
- def context
34
- Context.current
35
- end
36
-
37
- def logger
38
- @logger ||= Logger.new
39
- end
40
-
41
- # The configuration object.
42
- # @see Raven.configure
43
- def configuration
44
- @configuration ||= Configuration.new
45
- end
46
-
47
- # The client object is responsible for delivering formatted data to the Sentry server.
48
- def client
49
- @client ||= Client.new(configuration)
50
- end
51
-
52
- # Tell the log that the client is good to go
53
- def report_status
54
- return if client.configuration.silence_ready
55
- if client.configuration.send_in_current_environment?
56
- logger.info "Raven #{VERSION} ready to catch errors"
57
- else
58
- logger.info "Raven #{VERSION} configured not to send errors."
59
- end
60
- end
61
- alias_method :report_ready, :report_status
62
-
63
- # Call this method to modify defaults in your initializers.
64
- #
65
- # @example
66
- # Raven.configure do |config|
67
- # config.server = 'http://...'
68
- # end
69
- def configure
70
- yield(configuration) if block_given?
71
-
72
- self.client = Client.new(configuration)
73
- report_status
74
- self.client
75
- end
76
-
77
- # Send an event to the configured Sentry server
78
- #
79
- # @example
80
- # evt = Raven::Event.new(:message => "An error")
81
- # Raven.send_event(evt)
82
- def send_event(event)
83
- client.send_event(event)
84
- end
85
-
86
- # Capture and process any exceptions from the given block, or globally if
87
- # no block is given
88
- #
89
- # @example
90
- # Raven.capture do
91
- # MyApp.run
92
- # end
93
- def capture(options = {})
94
- if block_given?
95
- begin
96
- yield
97
- rescue Error
98
- raise # Don't capture Raven errors
99
- rescue Exception => e
100
- capture_exception(e, options)
101
- raise
102
- end
103
- else
104
- install_at_exit_hook(options)
105
- end
106
- end
107
-
108
- def capture_type(obj, options = {})
109
- return false unless should_capture?(obj)
110
- message_or_exc = obj.is_a?(String) ? "message" : "exception"
111
- if (evt = Event.send("from_" + message_or_exc, obj, options))
112
- yield evt if block_given?
113
- if configuration.async?
114
- configuration.async.call(evt)
115
- else
116
- send_event(evt)
117
- end
118
- Thread.current[:sentry_last_event_id] = evt.id
119
- evt
120
- end
121
- end
122
- alias_method :capture_message, :capture_type
123
- alias_method :capture_exception, :capture_type
124
-
125
- def last_event_id
126
- Thread.current[:sentry_last_event_id]
127
- end
128
-
129
- def should_capture?(message_or_exc)
130
- if configuration.should_capture
131
- configuration.should_capture.call(*[message_or_exc])
132
- else
133
- true
134
- end
135
- end
136
-
137
- # Provides extra context to the exception prior to it being handled by
138
- # Raven. An exception can have multiple annotations, which are merged
139
- # together.
140
- #
141
- # The options (annotation) is treated the same as the ``options``
142
- # parameter to ``capture_exception`` or ``Event.from_exception``, and
143
- # can contain the same ``:user``, ``:tags``, etc. options as these
144
- # methods.
145
- #
146
- # These will be merged with the ``options`` parameter to
147
- # ``Event.from_exception`` at the top of execution.
148
- #
149
- # @example
150
- # begin
151
- # raise "Hello"
152
- # rescue => exc
153
- # Raven.annotate_exception(exc, :user => { 'id' => 1,
154
- # 'email' => 'foo@example.com' })
155
- # end
156
- def annotate_exception(exc, options = {})
157
- notes = (exc.instance_variable_defined?(:@__raven_context) && exc.instance_variable_get(:@__raven_context)) || {}
158
- notes.merge!(options)
159
- exc.instance_variable_set(:@__raven_context, notes)
160
- exc
161
- end
162
-
163
- # Bind user context. Merges with existing context (if any).
164
- #
165
- # It is recommending that you send at least the ``id`` and ``email``
166
- # values. All other values are arbitrary.
167
- #
168
- # @example
169
- # Raven.user_context('id' => 1, 'email' => 'foo@example.com')
170
- def user_context(options = nil)
171
- self.context.user = options || {}
172
- end
29
+ extend Forwardable
173
30
 
174
- # Bind tags context. Merges with existing context (if any).
175
- #
176
- # Tags are key / value pairs which generally represent things like application version,
177
- # environment, role, and server names.
178
- #
179
- # @example
180
- # Raven.tags_context('my_custom_tag' => 'tag_value')
181
- def tags_context(options = nil)
182
- self.context.tags.merge!(options || {})
31
+ def instance
32
+ @instance ||= Raven::Instance.new
183
33
  end
184
34
 
185
- # Bind extra context. Merges with existing context (if any).
186
- #
187
- # Extra context shows up as Additional Data within Sentry, and is completely arbitrary.
188
- #
189
- # @example
190
- # Raven.extra_context('my_custom_data' => 'value')
191
- def extra_context(options = nil)
192
- self.context.extra.merge!(options || {})
193
- end
35
+ def_delegators :instance, :client=, :configuration=, :context, :logger, :configuration,
36
+ :client, :report_status, :configure, :send_event, :capture, :capture_type,
37
+ :last_event_id, :should_capture?, :annotate_exception, :user_context,
38
+ :tags_context, :extra_context, :rack_context, :breadcrumbs
194
39
 
195
- def rack_context(env)
196
- if env.empty?
197
- env = nil
198
- end
199
- self.context.rack_env = env
200
- end
40
+ def_delegator :instance, :report_status, :report_ready
41
+ def_delegator :instance, :capture_type, :capture_message
42
+ def_delegator :instance, :capture_type, :capture_exception
43
+ # For cross-language compatibility
44
+ def_delegator :instance, :capture_type, :captureException
45
+ def_delegator :instance, :capture_type, :captureMessage
46
+ def_delegator :instance, :annotate_exception, :annotateException
47
+ def_delegator :instance, :annotate_exception, :annotate
201
48
 
202
49
  # Injects various integrations. Default behavior: inject all available integrations
203
50
  def inject
@@ -232,29 +79,12 @@ module Raven
232
79
  self.logger.warn "Unable to load raven/integrations/#{integration}: #{error}"
233
80
  end
234
81
 
235
- def rails_safely_prepend(module_name, opts = {})
236
- return if opts[:to].nil?
82
+ def safely_prepend(module_name, opts = {})
83
+ return if opts[:to].nil? || opts[:from].nil?
237
84
  if opts[:to].respond_to?(:prepend, true)
238
- opts[:to].send(:prepend, Raven::Rails::Overrides.const_get(module_name))
85
+ opts[:to].send(:prepend, opts[:from].const_get(module_name))
239
86
  else
240
- opts[:to].send(:include, Raven::Rails::Overrides.const_get("Old" + module_name))
241
- end
242
- end
243
-
244
- # For cross-language compat
245
- alias :captureException :capture_exception
246
- alias :captureMessage :capture_message
247
- alias :annotateException :annotate_exception
248
- alias :annotate :annotate_exception
249
-
250
- private
251
-
252
- def install_at_exit_hook(options)
253
- at_exit do
254
- if $!
255
- logger.debug "Caught a post-mortem exception: #{$!.inspect}"
256
- capture_exception($!, options)
257
- end
87
+ opts[:to].send(:include, opts[:from].const_get("Old" + module_name))
258
88
  end
259
89
  end
260
90
  end
@@ -0,0 +1,74 @@
1
+ module Raven
2
+ class Breadcrumb
3
+ attr_accessor :category, :data, :message, :level, :timestamp, :type
4
+
5
+ def initialize
6
+ @category = nil
7
+ @data = {}
8
+ @level = nil
9
+ @message = nil
10
+ @timestamp = Time.now.to_i
11
+ @type = nil
12
+ end
13
+
14
+ def to_hash
15
+ {
16
+ :category => @category,
17
+ :data => @data,
18
+ :level => @level,
19
+ :message => @message,
20
+ :timestamp => @timestamp,
21
+ :type => @type
22
+ }
23
+ end
24
+ end
25
+ end
26
+
27
+ module Raven
28
+ class BreadcrumbBuffer
29
+ include Enumerable
30
+
31
+ def self.current
32
+ Thread.current[:sentry_breadcrumbs] ||= new
33
+ end
34
+
35
+ def self.clear!
36
+ Thread.current[:sentry_breadcrumbs] = nil
37
+ end
38
+
39
+ def initialize(size = 100)
40
+ @buffer = Array.new(size)
41
+ end
42
+
43
+ def record(crumb = nil)
44
+ if block_given?
45
+ crumb = Breadcrumb.new if crumb.nil?
46
+ yield(crumb)
47
+ end
48
+ @buffer.slice!(0)
49
+ @buffer << crumb
50
+ end
51
+
52
+ def members
53
+ @buffer.compact
54
+ end
55
+
56
+ def peek
57
+ members.last
58
+ end
59
+
60
+ def each(&block)
61
+ members.each(&block)
62
+ end
63
+
64
+ def empty?
65
+ !members.any?
66
+ end
67
+
68
+ def to_hash
69
+ {
70
+ :values => members.map(&:to_hash)
71
+ }
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,19 @@
1
+ module Raven
2
+ module ActiveSupportBreadcrumbs
3
+ class << self
4
+ def add(name, started, _finished, _unique_id, data)
5
+ Raven.breadcrumbs.record do |crumb|
6
+ crumb.data = data
7
+ crumb.category = name
8
+ crumb.timestamp = started.to_i
9
+ end
10
+ end
11
+
12
+ def inject
13
+ ActiveSupport::Notifications.subscribe(/.*/) do |name, started, finished, unique_id, data|
14
+ add(name, started, finished, unique_id, data)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,93 @@
1
+ require 'logger'
2
+
3
+ module Raven
4
+ module BreadcrumbLogger
5
+ LEVELS = {
6
+ ::Logger::DEBUG => 'debug',
7
+ ::Logger::INFO => 'info',
8
+ ::Logger::WARN => 'warn',
9
+ ::Logger::ERROR => 'error',
10
+ ::Logger::FATAL => 'fatal'
11
+ }.freeze
12
+
13
+ EXC_FORMAT = /^([a-zA-Z0-9]+)\:\s(.*)$/
14
+
15
+ def self.parse_exception(message)
16
+ lines = message.split(/\n\s*/)
17
+ # TODO: wat
18
+ return nil unless lines.length > 2
19
+
20
+ match = lines[0].match(EXC_FORMAT)
21
+ return nil unless match
22
+
23
+ _, type, value = match.to_a
24
+ [type, value]
25
+ end
26
+
27
+ def add(*args)
28
+ add_breadcrumb(*args)
29
+ super
30
+ end
31
+
32
+ def add_breadcrumb(severity, message = nil, progname = nil)
33
+ message = progname if message.nil? # see Ruby's Logger docs for why
34
+ return if ignored_logger?(progname)
35
+ return if message.nil? || message == ""
36
+
37
+ # some loggers will add leading/trailing space as they (incorrectly, mind you)
38
+ # think of logging as a shortcut to std{out,err}
39
+ message = message.strip
40
+
41
+ last_crumb = Raven.breadcrumbs.peek
42
+ # try to avoid dupes from logger broadcasts
43
+ if last_crumb.nil? || last_crumb.message != message
44
+ error = Raven::BreadcrumbLogger.parse_exception(message)
45
+ # TODO(dcramer): we need to filter out the "currently captured error"
46
+ if error
47
+ Raven.breadcrumbs.record do |crumb|
48
+ crumb.level = Raven::BreadcrumbLogger::LEVELS.fetch(severity, nil)
49
+ crumb.category = progname || 'error'
50
+ crumb.type = 'error'
51
+ crumb.data = {
52
+ :type => error[0],
53
+ :value => error[1]
54
+ }
55
+ end
56
+ else
57
+ Raven.breadcrumbs.record do |crumb|
58
+ crumb.level = Raven::BreadcrumbLogger::LEVELS.fetch(severity, nil)
59
+ crumb.category = progname || 'logger'
60
+ crumb.message = message
61
+ end
62
+ end
63
+ end
64
+ end
65
+
66
+ private
67
+
68
+ def ignored_logger?(progname)
69
+ progname == "sentry" ||
70
+ Raven.configuration.exclude_loggers.include?(progname)
71
+ end
72
+ end
73
+ module OldBreadcrumbLogger
74
+ def self.included(base)
75
+ base.class_eval do
76
+ include Raven::BreadcrumbLogger
77
+ alias_method :add_without_raven, :add
78
+ alias_method :add, :add_with_raven
79
+ end
80
+ end
81
+
82
+ def add_with_raven(*args)
83
+ add_breadcrumb(*args)
84
+ add_without_raven(*args)
85
+ end
86
+ end
87
+ end
88
+
89
+ Raven.safely_prepend(
90
+ "BreadcrumbLogger",
91
+ :from => Raven,
92
+ :to => ::Logger
93
+ )
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
- require 'zlib'
3
2
  require 'base64'
3
+ require 'json'
4
+ require 'zlib'
4
5
 
5
6
  require 'raven/version'
6
- require 'raven/okjson'
7
7
  require 'raven/transports/http'
8
8
 
9
9
  module Raven
@@ -73,7 +73,7 @@ module Raven
73
73
 
74
74
  def encode(event)
75
75
  hash = @processors.reduce(event.to_hash) { |memo, p| p.process(memo) }
76
- encoded = OkJson.encode(hash)
76
+ encoded = JSON.generate(hash)
77
77
 
78
78
  case configuration.encoding
79
79
  when 'gzip'