sentry-ruby-core 5.0.1 → 5.1.1

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
  SHA256:
3
- metadata.gz: 848bd1089194abb9be8a48b6c1c4c218dfba27a3555d8ae79a5ffe7d52a05569
4
- data.tar.gz: c52711ba4c4275d35cc4ed54a0dc0936422a63e199de91c08e3e6964fb80d6d6
3
+ metadata.gz: bfc28e6f57d57de669447d555889196617f72ce09624b343cd284375021f60f5
4
+ data.tar.gz: 5a6af91521df30a18ac1d021b2e59cd9e871653f1325cf6d65346e6c1dc91d7e
5
5
  SHA512:
6
- metadata.gz: fb55681acc83a4c970fbf8dd7c6715fbef7b6d26ad0a4ca674bc463de13881887d33dd8673772e60e25c2c66b6cb1f111a6c23936b1683d74384296924bb2b33
7
- data.tar.gz: 598c1571d00ca441b3a351dfe12c56967d3dae8e4f05b4a8f21b3e8fe16d0c94558e8dec5d211ff72aced12d5b433fb0f7f5f1b6ed522206d15847087dafd959
6
+ metadata.gz: fbe80e4c07f972d3babbf8c26c31cd97da84e91d77e86628a6931f35d7e66c83fb48461838d1a3e402361ee78dd9a3b6dfe849833f08a4573328d0eb4b5e66e1
7
+ data.tar.gz: 0f081f57f89207ece5279bd77fa89d311c3d516231421ccf60f4b9c232065a606d80eacc53ab460f12e087aa7b2fadfc4cdd4636863cb58261cc4e58a605ad2c
data/Gemfile CHANGED
@@ -9,6 +9,7 @@ gem "rake", "~> 12.0"
9
9
  gem "rspec", "~> 3.0"
10
10
  gem "rspec-retry"
11
11
  gem "webmock"
12
+ gem "fakeredis"
12
13
  gem "timecop"
13
14
  gem 'simplecov'
14
15
  gem "simplecov-cobertura", "~> 1.4"
data/lib/sentry/client.rb CHANGED
@@ -108,6 +108,7 @@ module Sentry
108
108
  event.contexts.merge!(trace: transaction.get_trace_context)
109
109
  event.timestamp = transaction.timestamp
110
110
  event.start_timestamp = transaction.start_timestamp
111
+ event.tags = transaction.tags
111
112
 
112
113
  finished_spans = transaction.span_recorder.spans.select { |span| span.timestamp && span != transaction }
113
114
  event.spans = finished_spans.map(&:to_hash)
@@ -75,6 +75,7 @@ module Sentry
75
75
  # An array of breadcrumbs loggers to be used. Available options are:
76
76
  # - :sentry_logger
77
77
  # - :http_logger
78
+ # - :redis_logger
78
79
  #
79
80
  # And if you also use sentry-rails:
80
81
  # - :active_support_logger
@@ -3,24 +3,47 @@
3
3
  module Sentry
4
4
  # @api private
5
5
  class Envelope
6
- def initialize(headers)
6
+ class Item
7
+ attr_accessor :headers, :payload
8
+
9
+ def initialize(headers, payload)
10
+ @headers = headers
11
+ @payload = payload
12
+ end
13
+
14
+ def type
15
+ @headers[:type] || 'event'
16
+ end
17
+
18
+ def to_s
19
+ <<~ITEM
20
+ #{JSON.generate(@headers)}
21
+ #{JSON.generate(@payload)}
22
+ ITEM
23
+ end
24
+ end
25
+
26
+ attr_accessor :headers, :items
27
+
28
+ def initialize(headers = {})
7
29
  @headers = headers
8
30
  @items = []
9
31
  end
10
32
 
11
33
  def add_item(headers, payload)
12
- @items << [headers, payload]
34
+ @items << Item.new(headers, payload)
13
35
  end
14
36
 
15
37
  def to_s
16
- payload = @items.map do |item_headers, item_payload|
17
- <<~ENVELOPE
18
- #{JSON.generate(item_headers)}
19
- #{JSON.generate(item_payload)}
20
- ENVELOPE
21
- end.join("\n")
22
-
23
- "#{JSON.generate(@headers)}\n#{payload}"
38
+ [JSON.generate(@headers), *@items.map(&:to_s)].join("\n")
39
+ end
40
+
41
+ def item_types
42
+ @items.map(&:type)
43
+ end
44
+
45
+ def event_id
46
+ @headers[:event_id]
24
47
  end
25
48
  end
26
49
  end
data/lib/sentry/hub.rb CHANGED
@@ -94,6 +94,8 @@ module Sentry
94
94
  def capture_exception(exception, **options, &block)
95
95
  check_argument_type!(exception, ::Exception)
96
96
 
97
+ return if Sentry.exception_captured?(exception)
98
+
97
99
  return unless current_client
98
100
 
99
101
  options[:hint] ||= {}
@@ -102,7 +104,10 @@ module Sentry
102
104
 
103
105
  return unless event
104
106
 
105
- capture_event(event, **options, &block)
107
+ capture_event(event, **options, &block).tap do
108
+ # mark the exception as captured so we can use this information to avoid duplicated capturing
109
+ exception.instance_variable_set(Sentry::CAPTURED_SIGNATURE, true)
110
+ end
106
111
  end
107
112
 
108
113
  def capture_message(message, **options, &block)
@@ -62,7 +62,7 @@ module Sentry
62
62
  self.url = request.scheme && request.url.split('?').first
63
63
  self.method = request.request_method
64
64
 
65
- self.headers = filter_and_format_headers(env)
65
+ self.headers = filter_and_format_headers(env, send_default_pii)
66
66
  self.env = filter_and_format_env(env, rack_env_whitelist)
67
67
  end
68
68
 
@@ -81,13 +81,14 @@ module Sentry
81
81
  e.message
82
82
  end
83
83
 
84
- def filter_and_format_headers(env)
84
+ def filter_and_format_headers(env, send_default_pii)
85
85
  env.each_with_object({}) do |(key, value), memo|
86
86
  begin
87
87
  key = key.to_s # rack env can contain symbols
88
88
  next memo['X-Request-Id'] ||= Utils::RequestId.read_from(env) if Utils::RequestId::REQUEST_ID_HEADERS.include?(key)
89
89
  next if is_server_protocol?(key, value, env["SERVER_PROTOCOL"])
90
90
  next if is_skippable_header?(key)
91
+ next if key == "HTTP_AUTHORIZATION" && !send_default_pii
91
92
 
92
93
  # Rack stores headers as HTTP_WHAT_EVER, we need What-Ever
93
94
  key = key.sub(/^HTTP_/, "")
@@ -6,7 +6,8 @@ module Sentry
6
6
  # @api private
7
7
  module Net
8
8
  module HTTP
9
- OP_NAME = "net.http"
9
+ OP_NAME = "http.client"
10
+ BREADCRUMB_CATEGORY = "net.http"
10
11
 
11
12
  # To explain how the entire thing works, we need to know how the original Net::HTTP#request works
12
13
  # Here's part of its definition. As you can see, it usually calls itself inside a #start block
@@ -53,7 +54,7 @@ module Sentry
53
54
 
54
55
  crumb = Sentry::Breadcrumb.new(
55
56
  level: :info,
56
- category: OP_NAME,
57
+ category: BREADCRUMB_CATEGORY,
57
58
  type: :info,
58
59
  data: {
59
60
  status: res.code.to_i,
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sentry
4
+ # @api private
5
+ class Redis
6
+ OP_NAME ||= "db.redis.command"
7
+ LOGGER_NAME ||= :redis_logger
8
+
9
+ def initialize(commands, host, port, db)
10
+ @commands, @host, @port, @db = commands, host, port, db
11
+ end
12
+
13
+ def instrument
14
+ return yield unless Sentry.initialized?
15
+
16
+ record_span do
17
+ yield.tap do
18
+ record_breadcrumb
19
+ end
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ attr_reader :commands, :host, :port, :db
26
+
27
+ def record_span
28
+ return yield unless (transaction = Sentry.get_current_scope.get_transaction) && transaction.sampled
29
+
30
+ sentry_span = transaction.start_child(op: OP_NAME, start_timestamp: Sentry.utc_now.to_f)
31
+
32
+ yield.tap do
33
+ sentry_span.set_description(commands_description)
34
+ sentry_span.set_data(:server, server_description)
35
+ sentry_span.set_timestamp(Sentry.utc_now.to_f)
36
+ end
37
+ end
38
+
39
+ def record_breadcrumb
40
+ return unless Sentry.configuration.breadcrumbs_logger.include?(LOGGER_NAME)
41
+
42
+ Sentry.add_breadcrumb(
43
+ Sentry::Breadcrumb.new(
44
+ level: :info,
45
+ category: OP_NAME,
46
+ type: :info,
47
+ data: {
48
+ commands: parsed_commands,
49
+ server: server_description
50
+ }
51
+ )
52
+ )
53
+ end
54
+
55
+ def commands_description
56
+ parsed_commands.map do |statement|
57
+ statement.values.join(" ").strip
58
+ end.join(", ")
59
+ end
60
+
61
+ def parsed_commands
62
+ commands.map do |statement|
63
+ command, key, *_values = statement
64
+
65
+ { command: command.to_s.upcase, key: key }
66
+ end
67
+ end
68
+
69
+ def server_description
70
+ "#{host}:#{port}/#{db}"
71
+ end
72
+
73
+ module Client
74
+ def logging(commands, &block)
75
+ Sentry::Redis.new(commands, host, port, db).instrument do
76
+ super
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
82
+
83
+ if defined?(::Redis::Client)
84
+ Sentry.register_patch do
85
+ patch = Sentry::Redis::Client
86
+ Redis::Client.prepend(patch) unless Redis::Client.ancestors.include?(patch)
87
+ end
88
+ end
data/lib/sentry/scope.rb CHANGED
@@ -173,7 +173,7 @@ module Sentry
173
173
  def set_contexts(contexts_hash)
174
174
  check_argument_type!(contexts_hash, Hash)
175
175
  @contexts.merge!(contexts_hash) do |key, old, new|
176
- new.merge(old)
176
+ old.merge(new)
177
177
  end
178
178
  end
179
179
 
@@ -164,13 +164,12 @@ module Sentry
164
164
  @name = UNLABELD_NAME
165
165
  end
166
166
 
167
- unless @sampled || @parent_sampled
167
+ if @sampled
168
+ event = hub.current_client.event_from_transaction(self)
169
+ hub.capture_event(event)
170
+ else
168
171
  hub.current_client.transport.record_lost_event(:sample_rate, 'transaction')
169
- return
170
172
  end
171
-
172
- event = hub.current_client.event_from_transaction(self)
173
- hub.capture_event(event)
174
173
  end
175
174
 
176
175
  protected
@@ -129,17 +129,14 @@ module Sentry
129
129
  def conn
130
130
  server = URI(@dsn.server)
131
131
 
132
- use_ssl = server.scheme == "https"
133
- port = use_ssl ? 443 : 80
134
-
135
132
  connection =
136
133
  if proxy = @transport_configuration.proxy
137
- ::Net::HTTP.new(server.hostname, port, proxy[:uri].hostname, proxy[:uri].port, proxy[:user], proxy[:password])
134
+ ::Net::HTTP.new(server.hostname, server.port, proxy[:uri].hostname, proxy[:uri].port, proxy[:user], proxy[:password])
138
135
  else
139
- ::Net::HTTP.new(server.hostname, port, nil)
136
+ ::Net::HTTP.new(server.hostname, server.port, nil)
140
137
  end
141
138
 
142
- connection.use_ssl = use_ssl
139
+ connection.use_ssl = server.scheme == "https"
143
140
  connection.read_timeout = @transport_configuration.timeout
144
141
  connection.write_timeout = @transport_configuration.timeout if connection.respond_to?(:write_timeout)
145
142
  connection.open_timeout = @transport_configuration.open_timeout
@@ -46,23 +46,19 @@ module Sentry
46
46
  end
47
47
 
48
48
  def send_event(event)
49
- event_hash = event.to_hash
50
- item_type = get_item_type(event_hash)
49
+ envelope = envelope_from_event(event)
50
+ send_envelope(envelope)
51
51
 
52
- if is_rate_limited?(item_type)
53
- log_info("Envelope [#{item_type}] not sent: rate limiting")
54
- record_lost_event(:ratelimit_backoff, item_type)
55
-
56
- return
57
- end
58
-
59
- encoded_data = encode(event)
52
+ event
53
+ end
60
54
 
61
- return nil unless encoded_data
55
+ def send_envelope(envelope)
56
+ reject_rate_limited_items(envelope)
62
57
 
63
- send_data(encoded_data)
58
+ return if envelope.items.empty?
64
59
 
65
- event
60
+ log_info("[Transport] Sending envelope with items [#{envelope.item_types.join(', ')}] #{envelope.event_id} to Sentry")
61
+ send_data(envelope.to_s)
66
62
  end
67
63
 
68
64
  def is_rate_limited?(item_type)
@@ -106,7 +102,7 @@ module Sentry
106
102
  'Sentry ' + fields.map { |key, value| "#{key}=#{value}" }.join(', ')
107
103
  end
108
104
 
109
- def encode(event)
105
+ def envelope_from_event(event)
110
106
  # Convert to hash
111
107
  event_payload = event.to_hash
112
108
  event_id = event_payload[:event_id] || event_payload["event_id"]
@@ -129,9 +125,8 @@ module Sentry
129
125
  client_report_headers, client_report_payload = fetch_pending_client_report
130
126
  envelope.add_item(client_report_headers, client_report_payload) if client_report_headers
131
127
 
132
- log_info("Sending envelope [#{item_type}] #{event_id} to Sentry")
133
128
 
134
- envelope.to_s
129
+ envelope
135
130
  end
136
131
 
137
132
  def record_lost_event(reason, item_type)
@@ -173,6 +168,19 @@ module Sentry
173
168
 
174
169
  [item_header, item_payload]
175
170
  end
171
+
172
+ def reject_rate_limited_items(envelope)
173
+ envelope.items.reject! do |item|
174
+ if is_rate_limited?(item.type)
175
+ log_info("[Transport] Envelope item [#{item.type}] not sent: rate limiting")
176
+ record_lost_event(:ratelimit_backoff, item.type)
177
+
178
+ true
179
+ else
180
+ false
181
+ end
182
+ end
183
+ end
176
184
  end
177
185
  end
178
186
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Sentry
4
- VERSION = "5.0.1"
4
+ VERSION = "5.1.1"
5
5
  end
data/lib/sentry-ruby.rb CHANGED
@@ -31,6 +31,8 @@ end
31
31
  module Sentry
32
32
  META = { "name" => "sentry.ruby", "version" => Sentry::VERSION }.freeze
33
33
 
34
+ CAPTURED_SIGNATURE = :@__sentry_captured
35
+
34
36
  LOGGER_PROGNAME = "sentry".freeze
35
37
 
36
38
  SENTRY_TRACE_HEADER_NAME = "sentry-trace".freeze
@@ -111,9 +113,17 @@ module Sentry
111
113
 
112
114
  # @!method configuration
113
115
  # @!macro configuration
116
+ def configuration
117
+ return unless initialized?
118
+ get_current_client.configuration
119
+ end
120
+
114
121
  # @!method send_event
115
122
  # @!macro send_event
116
- def_delegators :get_current_client, :configuration, :send_event
123
+ def send_event(*args)
124
+ return unless initialized?
125
+ get_current_client.send_event(*args)
126
+ end
117
127
 
118
128
  # @!macro [new] set_extras
119
129
  # Updates the scope's extras attribute by merging with the old value.
@@ -135,13 +145,31 @@ module Sentry
135
145
 
136
146
  # @!method set_tags
137
147
  # @!macro set_tags
148
+ def set_tags(*args)
149
+ return unless initialized?
150
+ get_current_scope.set_tags(*args)
151
+ end
152
+
138
153
  # @!method set_extras
139
154
  # @!macro set_extras
155
+ def set_extras(*args)
156
+ return unless initialized?
157
+ get_current_scope.set_extras(*args)
158
+ end
159
+
140
160
  # @!method set_user
141
161
  # @!macro set_user
162
+ def set_user(*args)
163
+ return unless initialized?
164
+ get_current_scope.set_user(*args)
165
+ end
166
+
142
167
  # @!method set_context
143
168
  # @!macro set_context
144
- def_delegators :get_current_scope, :set_tags, :set_extras, :set_user, :set_context
169
+ def set_context(*args)
170
+ return unless initialized?
171
+ get_current_scope.set_context(*args)
172
+ end
145
173
 
146
174
  ##### Main APIs #####
147
175
 
@@ -201,7 +229,8 @@ module Sentry
201
229
  #
202
230
  # @return [Breadcrumb, nil]
203
231
  def add_breadcrumb(breadcrumb, **options)
204
- get_current_hub&.add_breadcrumb(breadcrumb, **options)
232
+ return unless initialized?
233
+ get_current_hub.add_breadcrumb(breadcrumb, **options)
205
234
  end
206
235
 
207
236
  # Returns the current active hub.
@@ -221,14 +250,16 @@ module Sentry
221
250
  # Returns the current active client.
222
251
  # @return [Client, nil]
223
252
  def get_current_client
224
- get_current_hub&.current_client
253
+ return unless initialized?
254
+ get_current_hub.current_client
225
255
  end
226
256
 
227
257
  # Returns the current active scope.
228
258
  #
229
259
  # @return [Scope, nil]
230
260
  def get_current_scope
231
- get_current_hub&.current_scope
261
+ return unless initialized?
262
+ get_current_hub.current_scope
232
263
  end
233
264
 
234
265
  # Clones the main thread's active hub and stores it to the current thread.
@@ -250,7 +281,8 @@ module Sentry
250
281
  # @yieldparam scope [Scope]
251
282
  # @return [void]
252
283
  def configure_scope(&block)
253
- get_current_hub&.configure_scope(&block)
284
+ return unless initialized?
285
+ get_current_hub.configure_scope(&block)
254
286
  end
255
287
 
256
288
  # Takes a block and yields a temporary scope.
@@ -274,7 +306,8 @@ module Sentry
274
306
  # @yieldparam scope [Scope]
275
307
  # @return [void]
276
308
  def with_scope(&block)
277
- get_current_hub&.with_scope(&block)
309
+ return unless initialized?
310
+ get_current_hub.with_scope(&block)
278
311
  end
279
312
 
280
313
  # Takes an exception and reports it to Sentry via the currently active hub.
@@ -282,7 +315,8 @@ module Sentry
282
315
  # @yieldparam scope [Scope]
283
316
  # @return [Event, nil]
284
317
  def capture_exception(exception, **options, &block)
285
- get_current_hub&.capture_exception(exception, **options, &block)
318
+ return unless initialized?
319
+ get_current_hub.capture_exception(exception, **options, &block)
286
320
  end
287
321
 
288
322
  # Takes a message string and reports it to Sentry via the currently active hub.
@@ -290,30 +324,41 @@ module Sentry
290
324
  # @yieldparam scope [Scope]
291
325
  # @return [Event, nil]
292
326
  def capture_message(message, **options, &block)
293
- get_current_hub&.capture_message(message, **options, &block)
327
+ return unless initialized?
328
+ get_current_hub.capture_message(message, **options, &block)
294
329
  end
295
330
 
296
331
  # Takes an instance of Sentry::Event and dispatches it to the currently active hub.
297
332
  #
298
333
  # @return [Event, nil]
299
334
  def capture_event(event)
300
- get_current_hub&.capture_event(event)
335
+ return unless initialized?
336
+ get_current_hub.capture_event(event)
301
337
  end
302
338
 
303
339
  # Takes or initializes a new Sentry::Transaction and makes a sampling decision for it.
304
340
  #
305
341
  # @return [Transaction, nil]
306
342
  def start_transaction(**options)
307
- get_current_hub&.start_transaction(**options)
343
+ return unless initialized?
344
+ get_current_hub.start_transaction(**options)
308
345
  end
309
346
 
310
347
  # Returns the id of the lastly reported Sentry::Event.
311
348
  #
312
349
  # @return [String, nil]
313
350
  def last_event_id
314
- get_current_hub&.last_event_id
351
+ return unless initialized?
352
+ get_current_hub.last_event_id
315
353
  end
316
354
 
355
+ # Checks if the exception object has been captured by the SDK.
356
+ #
357
+ # @return [Boolean]
358
+ def exception_captured?(exc)
359
+ return false unless initialized?
360
+ !!exc.instance_variable_get(CAPTURED_SIGNATURE)
361
+ end
317
362
 
318
363
  ##### Helpers #####
319
364
 
@@ -344,3 +389,4 @@ end
344
389
 
345
390
  # patches
346
391
  require "sentry/net/http"
392
+ require "sentry/redis"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sentry-ruby-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.0.1
4
+ version: 5.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sentry Team
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-01-23 00:00:00.000000000 Z
11
+ date: 2022-02-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby
@@ -73,6 +73,7 @@ files:
73
73
  - lib/sentry/rack.rb
74
74
  - lib/sentry/rack/capture_exceptions.rb
75
75
  - lib/sentry/rake.rb
76
+ - lib/sentry/redis.rb
76
77
  - lib/sentry/release_detector.rb
77
78
  - lib/sentry/scope.rb
78
79
  - lib/sentry/span.rb