sentry-ruby-core 4.8.1 → 5.1.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.
@@ -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