sentry-ruby-core 4.8.1 → 5.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'faraday'
4
- require 'zlib'
3
+ require "net/http"
4
+ require "zlib"
5
5
 
6
6
  module Sentry
7
7
  class HTTPTransport < Transport
@@ -12,14 +12,13 @@ module Sentry
12
12
  DEFAULT_DELAY = 60
13
13
  RETRY_AFTER_HEADER = "retry-after"
14
14
  RATE_LIMIT_HEADER = "x-sentry-rate-limits"
15
-
16
- attr_reader :conn, :adapter
15
+ USER_AGENT = "sentry-ruby/#{Sentry::VERSION}"
17
16
 
18
17
  def initialize(*args)
19
18
  super
20
- @adapter = @transport_configuration.http_adapter || Faraday.default_adapter
21
- @conn = set_conn
22
19
  @endpoint = @dsn.envelope_endpoint
20
+
21
+ log_debug("Sentry HTTP Transport will connect to #{@dsn.server}")
23
22
  end
24
23
 
25
24
  def send_data(data)
@@ -30,29 +29,37 @@ module Sentry
30
29
  encoding = GZIP_ENCODING
31
30
  end
32
31
 
33
- response = conn.post @endpoint do |req|
34
- req.headers['Content-Type'] = CONTENT_TYPE
35
- req.headers['Content-Encoding'] = encoding
36
- req.headers['X-Sentry-Auth'] = generate_auth_header
37
- req.body = data
32
+ headers = {
33
+ 'Content-Type' => CONTENT_TYPE,
34
+ 'Content-Encoding' => encoding,
35
+ 'X-Sentry-Auth' => generate_auth_header,
36
+ 'User-Agent' => USER_AGENT
37
+ }
38
+
39
+ response = conn.start do |http|
40
+ request = ::Net::HTTP::Post.new(@endpoint, headers)
41
+ request.body = data
42
+ http.request(request)
38
43
  end
39
44
 
40
- if has_rate_limited_header?(response.headers)
41
- handle_rate_limited_response(response.headers)
42
- end
43
- rescue Faraday::Error => e
44
- error_info = e.message
45
+ if response.code.match?(/\A2\d{2}/)
46
+ if has_rate_limited_header?(response)
47
+ handle_rate_limited_response(response)
48
+ end
49
+ else
50
+ error_info = "the server responded with status #{response.code}"
45
51
 
46
- if e.response
47
- if e.response[:status] == 429
48
- handle_rate_limited_response(e.response[:headers])
52
+ if response.code == "429"
53
+ handle_rate_limited_response(response)
49
54
  else
50
- error_info += "\nbody: #{e.response[:body]}"
51
- error_info += " Error in headers is: #{e.response[:headers]['x-sentry-error']}" if e.response[:headers]['x-sentry-error']
55
+ error_info += "\nbody: #{response.body}"
56
+ error_info += " Error in headers is: #{response['x-sentry-error']}" if response['x-sentry-error']
52
57
  end
53
- end
54
58
 
55
- raise Sentry::ExternalError, error_info
59
+ raise Sentry::ExternalError, error_info
60
+ end
61
+ rescue SocketError => e
62
+ raise Sentry::ExternalError.new(e.message)
56
63
  end
57
64
 
58
65
  private
@@ -119,32 +126,36 @@ module Sentry
119
126
  @transport_configuration.encoding == GZIP_ENCODING && data.bytesize >= GZIP_THRESHOLD
120
127
  end
121
128
 
122
- def set_conn
123
- server = @dsn.server
129
+ def conn
130
+ server = URI(@dsn.server)
124
131
 
125
- log_debug("Sentry HTTP Transport connecting to #{server}")
132
+ connection =
133
+ if proxy = @transport_configuration.proxy
134
+ ::Net::HTTP.new(server.hostname, server.port, proxy[:uri].hostname, proxy[:uri].port, proxy[:user], proxy[:password])
135
+ else
136
+ ::Net::HTTP.new(server.hostname, server.port, nil)
137
+ end
126
138
 
127
- Faraday.new(server, :ssl => ssl_configuration, :proxy => @transport_configuration.proxy) do |builder|
128
- @transport_configuration.faraday_builder&.call(builder)
129
- builder.response :raise_error
130
- builder.options.merge! faraday_opts
131
- builder.headers[:user_agent] = "sentry-ruby/#{Sentry::VERSION}"
132
- builder.adapter(*adapter)
133
- end
134
- end
139
+ connection.use_ssl = server.scheme == "https"
140
+ connection.read_timeout = @transport_configuration.timeout
141
+ connection.write_timeout = @transport_configuration.timeout if connection.respond_to?(:write_timeout)
142
+ connection.open_timeout = @transport_configuration.open_timeout
135
143
 
136
- # TODO: deprecate and replace where possible w/Faraday Builder
137
- def faraday_opts
138
- [:timeout, :open_timeout].each_with_object({}) do |opt, memo|
139
- memo[opt] = @transport_configuration.public_send(opt) if @transport_configuration.public_send(opt)
144
+ ssl_configuration.each do |key, value|
145
+ connection.send("#{key}=", value)
140
146
  end
147
+
148
+ connection
141
149
  end
142
150
 
143
151
  def ssl_configuration
144
- {
152
+ configuration = {
145
153
  verify: @transport_configuration.ssl_verification,
146
154
  ca_file: @transport_configuration.ssl_ca_file
147
155
  }.merge(@transport_configuration.ssl || {})
156
+
157
+ configuration[:verify_mode] = configuration.delete(:verify) ? OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE
158
+ configuration
148
159
  end
149
160
  end
150
161
  end
@@ -23,7 +23,10 @@ module Sentry
23
23
 
24
24
  include LoggingHelper
25
25
 
26
- attr_reader :logger, :rate_limits, :discarded_events, :last_client_report_sent
26
+ attr_reader :rate_limits, :discarded_events, :last_client_report_sent
27
+
28
+ # @deprecated Use Sentry.logger to retrieve the current logger instead.
29
+ attr_reader :logger
27
30
 
28
31
  def initialize(configuration)
29
32
  @logger = configuration.logger
@@ -6,21 +6,21 @@ module Sentry
6
6
  message = "#{message}: #{exception.message}"
7
7
  message += "\n#{exception.backtrace.join("\n")}" if debug
8
8
 
9
- logger.error(LOGGER_PROGNAME) do
9
+ @logger.error(LOGGER_PROGNAME) do
10
10
  message
11
11
  end
12
12
  end
13
13
 
14
14
  def log_info(message)
15
- logger.info(LOGGER_PROGNAME) { message }
15
+ @logger.info(LOGGER_PROGNAME) { message }
16
16
  end
17
17
 
18
18
  def log_debug(message)
19
- logger.debug(LOGGER_PROGNAME) { message }
19
+ @logger.debug(LOGGER_PROGNAME) { message }
20
20
  end
21
21
 
22
22
  def log_warn(message)
23
- logger.warn(LOGGER_PROGNAME) { message }
23
+ @logger.warn(LOGGER_PROGNAME) { message }
24
24
  end
25
25
  end
26
26
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Sentry
4
- VERSION = "4.8.1"
4
+ VERSION = "5.1.0"
5
5
  end
data/lib/sentry-ruby.rb CHANGED
@@ -18,7 +18,6 @@ require "sentry/transaction"
18
18
  require "sentry/hub"
19
19
  require "sentry/background_worker"
20
20
 
21
-
22
21
  [
23
22
  "sentry/rake",
24
23
  "sentry/rack",
@@ -39,6 +38,7 @@ module Sentry
39
38
  THREAD_LOCAL = :sentry_hub
40
39
 
41
40
  class << self
41
+ # @!visibility private
42
42
  def exception_locals_tp
43
43
  @exception_locals_tp ||= TracePoint.new(:raise) do |tp|
44
44
  exception = tp.raised_exception
@@ -55,46 +55,126 @@ module Sentry
55
55
  end
56
56
  end
57
57
 
58
+ # @!attribute [rw] background_worker
59
+ # @return [BackgroundWorker]
58
60
  attr_accessor :background_worker
59
61
 
60
62
  ##### Patch Registration #####
61
- #
63
+
64
+ # @!visibility private
62
65
  def register_patch(&block)
63
66
  registered_patches << block
64
67
  end
65
68
 
69
+ # @!visibility private
66
70
  def apply_patches(config)
67
71
  registered_patches.each do |patch|
68
72
  patch.call(config)
69
73
  end
70
74
  end
71
75
 
76
+ # @!visibility private
72
77
  def registered_patches
73
78
  @registered_patches ||= []
74
79
  end
75
80
 
76
81
  ##### Integrations #####
77
- #
82
+
78
83
  # Returns a hash that contains all the integrations that have been registered to the main SDK.
84
+ #
85
+ # @return [Hash{String=>Hash}]
79
86
  def integrations
80
87
  @integrations ||= {}
81
88
  end
82
89
 
83
90
  # Registers the SDK integration with its name and version.
91
+ #
92
+ # @param name [String] name of the integration
93
+ # @param version [String] version of the integration
84
94
  def register_integration(name, version)
85
95
  meta = { name: "sentry.ruby.#{name}", version: version }.freeze
86
96
  integrations[name.to_s] = meta
87
97
  end
88
98
 
89
99
  ##### Method Delegation #####
90
- #
100
+
91
101
  extend Forwardable
92
102
 
93
- def_delegators :get_current_client, :configuration, :send_event
94
- def_delegators :get_current_scope, :set_tags, :set_extras, :set_user, :set_context
103
+ # @!macro [new] configuration
104
+ # The Configuration object that's used for configuring the client and its transport.
105
+ # @return [Configuration]
106
+ # @!macro [new] send_event
107
+ # Sends the event to Sentry.
108
+ # @param event [Event] the event to be sent.
109
+ # @param hint [Hash] the hint data that'll be passed to `before_send` callback.
110
+ # @return [Event]
111
+
112
+ # @!method configuration
113
+ # @!macro configuration
114
+ def configuration
115
+ return unless initialized?
116
+ get_current_client.configuration
117
+ end
118
+
119
+ # @!method send_event
120
+ # @!macro send_event
121
+ def send_event(*args)
122
+ return unless initialized?
123
+ get_current_client.send_event(*args)
124
+ end
125
+
126
+ # @!macro [new] set_extras
127
+ # Updates the scope's extras attribute by merging with the old value.
128
+ # @param extras [Hash]
129
+ # @return [Hash]
130
+ # @!macro [new] set_user
131
+ # Sets the scope's user attribute.
132
+ # @param user [Hash]
133
+ # @return [Hash]
134
+ # @!macro [new] set_context
135
+ # Adds a new key-value pair to current contexts.
136
+ # @param key [String, Symbol]
137
+ # @param value [Object]
138
+ # @return [Hash]
139
+ # @!macro [new] set_tags
140
+ # Updates the scope's tags attribute by merging with the old value.
141
+ # @param tags [Hash]
142
+ # @return [Hash]
143
+
144
+ # @!method set_tags
145
+ # @!macro set_tags
146
+ def set_tags(*args)
147
+ return unless initialized?
148
+ get_current_scope.set_tags(*args)
149
+ end
150
+
151
+ # @!method set_extras
152
+ # @!macro set_extras
153
+ def set_extras(*args)
154
+ return unless initialized?
155
+ get_current_scope.set_extras(*args)
156
+ end
157
+
158
+ # @!method set_user
159
+ # @!macro set_user
160
+ def set_user(*args)
161
+ return unless initialized?
162
+ get_current_scope.set_user(*args)
163
+ end
164
+
165
+ # @!method set_context
166
+ # @!macro set_context
167
+ def set_context(*args)
168
+ return unless initialized?
169
+ get_current_scope.set_context(*args)
170
+ end
95
171
 
96
172
  ##### Main APIs #####
173
+
174
+ # Initializes the SDK with given configuration.
97
175
  #
176
+ # @yieldparam config [Configuration]
177
+ # @return [void]
98
178
  def init(&block)
99
179
  config = Configuration.new
100
180
  yield(config) if block_given?
@@ -116,31 +196,46 @@ module Sentry
116
196
  end
117
197
  end
118
198
 
199
+ # Returns true if the SDK is initialized.
200
+ #
201
+ # @return [Boolean]
202
+ def initialized?
203
+ !!get_main_hub
204
+ end
205
+
119
206
  # Returns an uri for security policy reporting that's generated from the given DSN
120
207
  # (To learn more about security policy reporting: https://docs.sentry.io/product/security-policy-reporting/)
121
208
  #
122
209
  # It returns nil if
210
+ # - The SDK is not initialized yet.
211
+ # - The DSN is not provided or is invalid.
123
212
  #
124
- # 1. The SDK is not initialized yet.
125
- # 2. The DSN is not provided or is invalid.
213
+ # @return [String, nil]
126
214
  def csp_report_uri
127
215
  return unless initialized?
128
216
  configuration.csp_report_uri
129
217
  end
130
218
 
131
219
  # Returns the main thread's active hub.
220
+ #
221
+ # @return [Hub]
132
222
  def get_main_hub
133
223
  @main_hub
134
224
  end
135
225
 
136
226
  # Takes an instance of Sentry::Breadcrumb and stores it to the current active scope.
227
+ #
228
+ # @return [Breadcrumb, nil]
137
229
  def add_breadcrumb(breadcrumb, **options)
138
- get_current_hub&.add_breadcrumb(breadcrumb, **options)
230
+ return unless initialized?
231
+ get_current_hub.add_breadcrumb(breadcrumb, **options)
139
232
  end
140
233
 
141
234
  # Returns the current active hub.
142
235
  # If the current thread doesn't have an active hub, it will clone the main thread's active hub,
143
236
  # stores it in the current thread, and then returns it.
237
+ #
238
+ # @return [Hub]
144
239
  def get_current_hub
145
240
  # we need to assign a hub to the current thread if it doesn't have one yet
146
241
  #
@@ -151,85 +246,114 @@ module Sentry
151
246
  end
152
247
 
153
248
  # Returns the current active client.
249
+ # @return [Client, nil]
154
250
  def get_current_client
155
- get_current_hub&.current_client
251
+ return unless initialized?
252
+ get_current_hub.current_client
156
253
  end
157
254
 
158
255
  # Returns the current active scope.
256
+ #
257
+ # @return [Scope, nil]
159
258
  def get_current_scope
160
- get_current_hub&.current_scope
259
+ return unless initialized?
260
+ get_current_hub.current_scope
161
261
  end
162
262
 
163
263
  # Clones the main thread's active hub and stores it to the current thread.
264
+ #
265
+ # @return [void]
164
266
  def clone_hub_to_current_thread
165
267
  Thread.current.thread_variable_set(THREAD_LOCAL, get_main_hub.clone)
166
268
  end
167
269
 
168
270
  # Takes a block and yields the current active scope.
169
271
  #
170
- # ```ruby
171
- # Sentry.configure_scope do |scope|
172
- # scope.set_tags(foo: "bar")
173
- # end
272
+ # @example
273
+ # Sentry.configure_scope do |scope|
274
+ # scope.set_tags(foo: "bar")
275
+ # end
174
276
  #
175
- # Sentry.capture_message("test message") # this event will have tags { foo: "bar" }
176
- # ```
277
+ # Sentry.capture_message("test message") # this event will have tags { foo: "bar" }
177
278
  #
279
+ # @yieldparam scope [Scope]
280
+ # @return [void]
178
281
  def configure_scope(&block)
179
- get_current_hub&.configure_scope(&block)
282
+ return unless initialized?
283
+ get_current_hub.configure_scope(&block)
180
284
  end
181
285
 
182
286
  # Takes a block and yields a temporary scope.
183
287
  # The temporary scope will inherit all the attributes from the current active scope and replace it to be the active
184
- # scope inside the block. For example:
288
+ # scope inside the block.
185
289
  #
186
- # ```ruby
187
- # Sentry.configure_scope do |scope|
188
- # scope.set_tags(foo: "bar")
189
- # end
290
+ # @example
291
+ # Sentry.configure_scope do |scope|
292
+ # scope.set_tags(foo: "bar")
293
+ # end
190
294
  #
191
- # Sentry.capture_message("test message") # this event will have tags { foo: "bar" }
295
+ # Sentry.capture_message("test message") # this event will have tags { foo: "bar" }
192
296
  #
193
- # Sentry.with_scope do |temp_scope|
194
- # temp_scope.set_tags(foo: "baz")
195
- # Sentry.capture_message("test message 2") # this event will have tags { foo: "baz" }
196
- # end
297
+ # Sentry.with_scope do |temp_scope|
298
+ # temp_scope.set_tags(foo: "baz")
299
+ # Sentry.capture_message("test message 2") # this event will have tags { foo: "baz" }
300
+ # end
197
301
  #
198
- # Sentry.capture_message("test message 3") # this event will have tags { foo: "bar" }
199
- # ```
302
+ # Sentry.capture_message("test message 3") # this event will have tags { foo: "bar" }
200
303
  #
304
+ # @yieldparam scope [Scope]
305
+ # @return [void]
201
306
  def with_scope(&block)
202
- get_current_hub&.with_scope(&block)
307
+ return unless initialized?
308
+ get_current_hub.with_scope(&block)
203
309
  end
204
310
 
205
311
  # Takes an exception and reports it to Sentry via the currently active hub.
312
+ #
313
+ # @yieldparam scope [Scope]
314
+ # @return [Event, nil]
206
315
  def capture_exception(exception, **options, &block)
207
- get_current_hub&.capture_exception(exception, **options, &block)
316
+ return unless initialized?
317
+ get_current_hub.capture_exception(exception, **options, &block)
208
318
  end
209
319
 
210
320
  # Takes a message string and reports it to Sentry via the currently active hub.
321
+ #
322
+ # @yieldparam scope [Scope]
323
+ # @return [Event, nil]
211
324
  def capture_message(message, **options, &block)
212
- get_current_hub&.capture_message(message, **options, &block)
325
+ return unless initialized?
326
+ get_current_hub.capture_message(message, **options, &block)
213
327
  end
214
328
 
215
329
  # Takes an instance of Sentry::Event and dispatches it to the currently active hub.
330
+ #
331
+ # @return [Event, nil]
216
332
  def capture_event(event)
217
- get_current_hub&.capture_event(event)
333
+ return unless initialized?
334
+ get_current_hub.capture_event(event)
218
335
  end
219
336
 
220
337
  # Takes or initializes a new Sentry::Transaction and makes a sampling decision for it.
338
+ #
339
+ # @return [Transaction, nil]
221
340
  def start_transaction(**options)
222
- get_current_hub&.start_transaction(**options)
341
+ return unless initialized?
342
+ get_current_hub.start_transaction(**options)
223
343
  end
224
344
 
225
345
  # Returns the id of the lastly reported Sentry::Event.
346
+ #
347
+ # @return [String, nil]
226
348
  def last_event_id
227
- get_current_hub&.last_event_id
349
+ return unless initialized?
350
+ get_current_hub.last_event_id
228
351
  end
229
352
 
230
353
 
231
354
  ##### Helpers #####
232
- #
355
+
356
+ # @!visibility private
233
357
  def sys_command(command)
234
358
  result = `#{command} 2>&1` rescue nil
235
359
  return if result.nil? || result.empty? || ($CHILD_STATUS && $CHILD_STATUS.exitstatus != 0)
@@ -237,18 +361,17 @@ module Sentry
237
361
  result.strip
238
362
  end
239
363
 
240
- def initialized?
241
- !!@main_hub
242
- end
243
-
364
+ # @!visibility private
244
365
  def logger
245
366
  configuration.logger
246
367
  end
247
368
 
369
+ # @!visibility private
248
370
  def sdk_meta
249
371
  META
250
372
  end
251
373
 
374
+ # @!visibility private
252
375
  def utc_now
253
376
  Time.now.utc
254
377
  end
@@ -257,3 +380,4 @@ end
257
380
 
258
381
  # patches
259
382
  require "sentry/net/http"
383
+ require "sentry/redis"
@@ -22,6 +22,5 @@ Gem::Specification.new do |spec|
22
22
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
23
23
  spec.require_paths = ["lib"]
24
24
 
25
- spec.add_dependency "faraday"
26
25
  spec.add_dependency "concurrent-ruby"
27
26
  end
data/sentry-ruby.gemspec CHANGED
@@ -18,6 +18,5 @@ Gem::Specification.new do |spec|
18
18
  spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/master/CHANGELOG.md"
19
19
 
20
20
  spec.add_dependency "sentry-ruby-core", Sentry::VERSION
21
- spec.add_dependency "faraday", ">= 1.0"
22
21
  spec.add_dependency "concurrent-ruby", '~> 1.0', '>= 1.0.2'
23
22
  end
metadata CHANGED
@@ -1,29 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sentry-ruby-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.8.1
4
+ version: 5.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sentry Team
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-11-22 00:00:00.000000000 Z
11
+ date: 2022-02-10 00:00:00.000000000 Z
12
12
  dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: faraday
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - ">="
18
- - !ruby/object:Gem::Version
19
- version: '0'
20
- type: :runtime
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - ">="
25
- - !ruby/object:Gem::Version
26
- version: '0'
27
13
  - !ruby/object:Gem::Dependency
28
14
  name: concurrent-ruby
29
15
  requirement: !ruby/object:Gem::Requirement
@@ -48,6 +34,7 @@ extra_rdoc_files:
48
34
  files:
49
35
  - ".gitignore"
50
36
  - ".rspec"
37
+ - ".yardopts"
51
38
  - CHANGELOG.md
52
39
  - CODE_OF_CONDUCT.md
53
40
  - Gemfile
@@ -86,6 +73,7 @@ files:
86
73
  - lib/sentry/rack.rb
87
74
  - lib/sentry/rack/capture_exceptions.rb
88
75
  - lib/sentry/rake.rb
76
+ - lib/sentry/redis.rb
89
77
  - lib/sentry/release_detector.rb
90
78
  - lib/sentry/scope.rb
91
79
  - lib/sentry/span.rb