sentry-ruby 4.0.1 → 4.1.4

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
  SHA256:
3
- metadata.gz: 732989200a2cbf0a935c386cf84037d655332953ea2fef828099fbf68b756ab9
4
- data.tar.gz: 4aecc66958496eede273d4cc4b2e3861e0ce7eb3cf769912a4a6675efeba693e
3
+ metadata.gz: a99aa0cd23f84ac85fe92aeff13f70aabb29f8b4f7e5239b48806f66e9af5376
4
+ data.tar.gz: bb5c2b32b2086f913fd6e8f55566b9a6bb6331aae414922e65696e4eb87457fe
5
5
  SHA512:
6
- metadata.gz: 798be41100ec3c8eb09039f9432df02fd0de323427fd0d8bf6d9ced8f7b39d420e972c6006bcb4490002e32f03acfad2311b8ddab826c6927bd53b2a7316c7cb
7
- data.tar.gz: 78cc7ec6d5c1a7f6603f9f2e4917b26e12ad5b13c0387a00645282acf8c910518c4132c171e2c9a8311f11e82afa3eb0454d2102bf48abd4c44d6625fd4ce4cf
6
+ metadata.gz: 70c330fa68f5a42588f7dfa815b7e2276b508de5c3a606c058d37afff5ef3b50ba3c3071cbb51356fd859c97ca19ffdd95a986931e4f54b752ad2b11b352feae
7
+ data.tar.gz: 0d0dde39080876a24f52256c503778226ae53bf0a668ba55503ae6c76bb4d0731b1cb7c775029fd2ec258c5e4eacbcb679272d7dd9495831ac1f6b77d56124ff
@@ -1,5 +1,84 @@
1
1
  # Changelog
2
2
 
3
+ ## 4.1.4
4
+
5
+ - Fix headers serialization for sentry-ruby [#1197](https://github.com/getsentry/sentry-ruby/pull/1197) (by @moofkit)
6
+ - Support capturing "sentry-trace" header from the middleware [#1205](https://github.com/getsentry/sentry-ruby/pull/1205)
7
+ - Document public APIs on the Sentry module [#1208](https://github.com/getsentry/sentry-ruby/pull/1208)
8
+ - Check the argument type of capture_exception and capture_event helpers [#1209](https://github.com/getsentry/sentry-ruby/pull/1209)
9
+
10
+ ## 4.1.3
11
+
12
+ - rm reference to old constant (from Rails v2.2) [#1184](https://github.com/getsentry/sentry-ruby/pull/1184)
13
+ - Use copied env in events [#1186](https://github.com/getsentry/sentry-ruby/pull/1186)
14
+ - Fixes [#1183](https://github.com/getsentry/sentry-ruby/issues/1183)
15
+ - Refactor RequestInterface [#1187](https://github.com/getsentry/sentry-ruby/pull/1187)
16
+ - Supply event hint to async callback when possible [#1189](https://github.com/getsentry/sentry-ruby/pull/1189)
17
+ - Fixes [#1188](https://github.com/getsentry/sentry-ruby/issues/1188)
18
+ - Refactor stacktrace parsing and increase test coverage [#1190](https://github.com/getsentry/sentry-ruby/pull/1190)
19
+ - Sentry.send_event should also take a hint [#1192](https://github.com/getsentry/sentry-ruby/pull/1192)
20
+
21
+ ## 4.1.2
22
+
23
+ - before_send callback shouldn't be applied to transaction events [#1167](https://github.com/getsentry/sentry-ruby/pull/1167)
24
+ - Transaction improvements [#1170](https://github.com/getsentry/sentry-ruby/pull/1170)
25
+ - Support Ruby 3 [#1172](https://github.com/getsentry/sentry-ruby/pull/1172)
26
+ - Add Integrable module [#1177](https://github.com/getsentry/sentry-ruby/pull/1177)
27
+
28
+ ## 4.1.1
29
+
30
+ - Fix NoMethodError when sending is not allowed [#1161](https://github.com/getsentry/sentry-ruby/pull/1161)
31
+ - Add notification for users who still use deprecated middlewares [#1160](https://github.com/getsentry/sentry-ruby/pull/1160)
32
+ - Improve top-level api safety [#1162](https://github.com/getsentry/sentry-ruby/pull/1162)
33
+
34
+ ## 4.1.0
35
+
36
+ - Separate rack integration [#1138](https://github.com/getsentry/sentry-ruby/pull/1138)
37
+ - Fixes [#1136](https://github.com/getsentry/sentry-ruby/pull/1136)
38
+ - Fix event sampling [#1144](https://github.com/getsentry/sentry-ruby/pull/1144)
39
+ - Merge & rename 2 Rack middlewares [#1147](https://github.com/getsentry/sentry-ruby/pull/1147)
40
+ - Fixes [#1153](https://github.com/getsentry/sentry-ruby/pull/1153)
41
+ - Removed `Sentry::Rack::Tracing` middleware and renamed `Sentry::Rack::CaptureException` to `Sentry::Rack::CaptureExceptions`.
42
+ - Deep-copy spans [#1148](https://github.com/getsentry/sentry-ruby/pull/1148)
43
+ - Move span recorder related code from Span to Transaction [#1149](https://github.com/getsentry/sentry-ruby/pull/1149)
44
+ - Check SDK initialization before running integrations [#1151](https://github.com/getsentry/sentry-ruby/pull/1151)
45
+ - Fixes [#1145](https://github.com/getsentry/sentry-ruby/pull/1145)
46
+ - Refactor transport [#1154](https://github.com/getsentry/sentry-ruby/pull/1154)
47
+ - Implement non-blocking event sending [#1155](https://github.com/getsentry/sentry-ruby/pull/1155)
48
+ - Added `background_worker_threads` configuration option.
49
+
50
+ ### Noticeable Changes
51
+
52
+ #### Middleware Changes
53
+
54
+ `Sentry::Rack::Tracing` is now removed. And `Sentry::Rack::CaptureException` has been renamed to `Sentry::Rack::CaptureExceptions`.
55
+
56
+ #### Events Are Sent Asynchronously
57
+
58
+ `sentry-ruby` now sends events asynchronously by default. The functionality works like this:
59
+
60
+ 1. When the SDK is initialized, a `Sentry::BackgroundWorker` will be initialized too.
61
+ 2. When an event is passed to `Client#capture_event`, instead of sending it directly with `Client#send_event`, we'll let the worker do it.
62
+ 3. The worker will have a number of threads. And the one of the idle threads will pick the job and call `Client#send_event`.
63
+ - If all the threads are busy, new jobs will be put into a queue, which has a limit of 30.
64
+ - If the queue size is exceeded, new events will be dropped.
65
+
66
+ However, if you still prefer to use your own async approach, that's totally fine. If you have `config.async` set, the worker won't initialize a thread pool and won't be used either.
67
+
68
+ This functionality also introduces a new `background_worker_threads` config option. It allows you to decide how many threads should the worker hold. By default, the value will be the number of the processors your machine has. For example, if your machine has 4 processors, the value would be 4.
69
+
70
+ Of course, you can always override the value to fit your use cases, like
71
+
72
+ ```ruby
73
+ config.background_worker_threads = 5 # the worker will have 5 threads for sending events
74
+ ```
75
+
76
+ You can also disable this new non-blocking behaviour by giving a `0` value:
77
+
78
+ ```ruby
79
+ config.background_worker_threads = 0 # all events will be sent synchronously
80
+ ```
81
+
3
82
  ## 4.0.1
4
83
 
5
84
  - Add rake integration: [1137](https://github.com/getsentry/sentry-ruby/pull/1137)
data/Gemfile CHANGED
@@ -5,10 +5,10 @@ gemspec
5
5
 
6
6
  gem "rake", "~> 12.0"
7
7
  gem "rspec", "~> 3.0"
8
- gem "codecov"
8
+ gem "codecov", "0.2.12"
9
9
 
10
10
  gem "pry"
11
- gem "rack"
11
+ gem "rack" unless ENV["WITHOUT_RACK"] == "1"
12
12
 
13
13
  gem "benchmark-ips"
14
14
  gem "benchmark_driver"
data/README.md CHANGED
@@ -94,7 +94,7 @@ Sentry.init do |config|
94
94
  end
95
95
  ```
96
96
 
97
- To lean more about performance monitoring, please visit the [official documentation](https://docs.sentry.io/platforms/ruby/performance).
97
+ To learn more about performance monitoring, please visit the [official documentation](https://docs.sentry.io/platforms/ruby/performance).
98
98
 
99
99
  ### Usage
100
100
 
@@ -115,8 +115,7 @@ Sentry.init do |config|
115
115
  end
116
116
  end
117
117
 
118
- use Sentry::Rack::Tracing # this needs to be placed first
119
- use Sentry::Rack::CaptureException
118
+ use Sentry::Rack::CaptureExceptions
120
119
  ```
121
120
 
122
121
  Otherwise, Sentry you can always use the capture helpers manually
@@ -140,13 +139,13 @@ We also provide integrations with popular frameworks/libraries with the related
140
139
 
141
140
  You're all set - but there's a few more settings you may want to know about too!
142
141
 
143
- #### async
142
+ #### Blocking v.s. Non-blocking
144
143
 
145
- When an error or message occurs, the notification is immediately sent to Sentry. Sentry can be configured to send asynchronously:
144
+ **Before version 4.1.0**, `sentry-ruby` sends every event immediately. But it can be configured to send asynchronously:
146
145
 
147
146
  ```ruby
148
- config.async = lambda { |event|
149
- Thread.new { Sentry.send_event(event) }
147
+ config.async = lambda { |event, hint|
148
+ Thread.new { Sentry.send_event(event, hint) }
150
149
  }
151
150
  ```
152
151
 
@@ -155,17 +154,52 @@ Using a thread to send events will be adequate for truly parallel Ruby platforms
155
154
  Note that the naive example implementation has a major drawback - it can create an infinite number of threads. We recommend creating a background job, using your background job processor, that will send Sentry notifications in the background.
156
155
 
157
156
  ```ruby
158
- config.async = lambda { |event| SentryJob.perform_later(event) }
157
+ config.async = lambda { |event, hint| SentryJob.perform_later(event, hint) }
159
158
 
160
159
  class SentryJob < ActiveJob::Base
161
160
  queue_as :default
162
161
 
163
- def perform(event)
164
- Sentry.send_event(event)
162
+ def perform(event, hint)
163
+ Sentry.send_event(event, hint)
165
164
  end
166
165
  end
167
166
  ```
168
167
 
168
+
169
+ **After version 4.1.0**, `sentry-ruby` sends events asynchronously by default. The functionality works like this:
170
+
171
+ 1. When the SDK is initialized, a `Sentry::BackgroundWorker` will be initialized too.
172
+ 2. When an event is passed to `Client#capture_event`, instead of sending it directly with `Client#send_event`, we'll let the worker do it.
173
+ 3. The worker will have a number of threads. And the one of the idle threads will pick the job and call `Client#send_event`.
174
+ - If all the threads are busy, new jobs will be put into a queue, which has a limit of 30.
175
+ - If the queue size is exceeded, new events will be dropped.
176
+
177
+ However, if you still prefer to use your own async approach, that's totally fine. If you have `config.async` set, the worker won't initialize a thread pool and won't be used either.
178
+
179
+ ##### About `Sentry::BackgroundWorker`
180
+
181
+ - The worker is built on top of the [concurrent-ruby](https://github.com/ruby-concurrency/concurrent-ruby) gem's [ThreadPoolExecutor](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/ThreadPoolExecutor.html), which is also used by Rails ActiveJob's async adapter. This should minimize the risk of messing up client applications with our own thread pool implementaion.
182
+
183
+ This functionality also introduces a new `background_worker_threads` config option. It allows you to decide how many threads should the worker hold. By default, the value will be the number of the processors your machine has. For example, if your machine has 4 processors, the value would be 4.
184
+
185
+ Of course, you can always override the value to fit your use cases, like
186
+
187
+ ```ruby
188
+ config.background_worker_threads = 5 # the worker will have 5 threads for sending events
189
+ ```
190
+
191
+ You can also disable this new non-blocking behaviour by giving a `0` value:
192
+
193
+ ```ruby
194
+ config.background_worker_threads = 0 # all events will be sent synchronously
195
+ ```
196
+
197
+ If you want to send a particular event immediately, you can use event hints to do it:
198
+
199
+ ```ruby
200
+ Sentry.capture_message("send me now!", hint: { background: false })
201
+ ```
202
+
169
203
  #### Contexts
170
204
 
171
205
  In sentry-ruby, every event will inherit their contextual data from the current scope. So you can enrich the event's data by configuring the current scope like:
@@ -191,7 +225,6 @@ Or use top-level setters
191
225
  Sentry.set_user(id: 1, email: "test@example.com")
192
226
  Sentry.set_tags(tag_1: "foo", tag_2: "bar")
193
227
  Sentry.set_extras(order_number: 1234, tickets_count: 4)
194
-
195
228
  ```
196
229
 
197
230
  Or build up a temporary scope for local information:
@@ -222,3 +255,4 @@ Sentry.capture_exception(exception, tags: {foo: "bar"})
222
255
  * [Bug Tracker](https://github.com/getsentry/sentry-ruby/issues)
223
256
  * [Forum](https://forum.sentry.io/)
224
257
  - [Discord](https://discord.gg/ez5KZN7)
258
+
@@ -1,7 +1,9 @@
1
1
  require "forwardable"
2
+ require "time"
2
3
 
3
4
  require "sentry/version"
4
5
  require "sentry/core_ext/object/deep_dup"
6
+ require "sentry/utils/argument_checking_helper"
5
7
  require "sentry/configuration"
6
8
  require "sentry/logger"
7
9
  require "sentry/event"
@@ -9,7 +11,7 @@ require "sentry/transaction_event"
9
11
  require "sentry/span"
10
12
  require "sentry/transaction"
11
13
  require "sentry/hub"
12
- require "sentry/rack"
14
+ require "sentry/background_worker"
13
15
 
14
16
  def safely_require(lib)
15
17
  begin
@@ -19,6 +21,7 @@ def safely_require(lib)
19
21
  end
20
22
 
21
23
  safely_require "sentry/rake"
24
+ safely_require "sentry/rack"
22
25
 
23
26
  module Sentry
24
27
  class Error < StandardError
@@ -38,11 +41,27 @@ module Sentry
38
41
  Time.now.utc
39
42
  end
40
43
 
44
+ class << self
45
+ # Returns a hash that contains all the integrations that have been registered to the main SDK.
46
+ def integrations
47
+ @integrations ||= {}
48
+ end
49
+
50
+ # Registers the SDK integration with its name and version.
51
+ def register_integration(name, version)
52
+ meta = { name: "sentry.ruby.#{name}", version: version }.freeze
53
+ integrations[name.to_s] = meta
54
+ end
55
+ end
56
+
41
57
  class << self
42
58
  extend Forwardable
43
59
 
60
+ def_delegators :get_current_client, :configuration, :send_event
44
61
  def_delegators :get_current_scope, :set_tags, :set_extras, :set_user
45
62
 
63
+ attr_accessor :background_worker
64
+
46
65
  def init(&block)
47
66
  config = Configuration.new
48
67
  yield(config)
@@ -51,28 +70,22 @@ module Sentry
51
70
  hub = Hub.new(client, scope)
52
71
  Thread.current[THREAD_LOCAL] = hub
53
72
  @main_hub = hub
73
+ @background_worker = Sentry::BackgroundWorker.new(config)
54
74
  end
55
75
 
76
+ # Returns the main thread's active hub.
56
77
  def get_main_hub
57
78
  @main_hub
58
79
  end
59
80
 
60
- def logger
61
- configuration.logger
62
- end
63
-
64
- def add_breadcrumb(breadcrumb, &block)
65
- get_current_scope.breadcrumbs.record(breadcrumb, &block)
66
- end
67
-
68
- def configuration
69
- get_current_client.configuration
70
- end
71
-
72
- def get_current_client
73
- get_current_hub.current_client
81
+ # Takes an instance of Sentry::Breadcrumb and stores it to the current active scope.
82
+ def add_breadcrumb(breadcrumb)
83
+ get_current_scope.breadcrumbs.record(breadcrumb)
74
84
  end
75
85
 
86
+ # Returns the current active hub.
87
+ # If the current thread doesn't have an active hub, it will clone the main thread's active hub,
88
+ # stores it in the current thread, and then returns it.
76
89
  def get_current_hub
77
90
  # we need to assign a hub to the current thread if it doesn't have one yet
78
91
  #
@@ -82,44 +95,81 @@ module Sentry
82
95
  Thread.current[THREAD_LOCAL] || clone_hub_to_current_thread
83
96
  end
84
97
 
85
- def clone_hub_to_current_thread
86
- Thread.current[THREAD_LOCAL] = get_main_hub.clone
98
+ # Returns the current active client.
99
+ def get_current_client
100
+ get_current_hub&.current_client
87
101
  end
88
102
 
103
+ # Returns the current active scope.
89
104
  def get_current_scope
90
- get_current_hub.current_scope
105
+ get_current_hub&.current_scope
91
106
  end
92
107
 
93
- def with_scope(&block)
94
- get_current_hub.with_scope(&block)
108
+ # Clones the main thread's active hub and stores it to the current thread.
109
+ def clone_hub_to_current_thread
110
+ Thread.current[THREAD_LOCAL] = get_main_hub.clone
95
111
  end
96
112
 
113
+ # Takes a block and yields the current active scope.
114
+ #
115
+ # ```ruby
116
+ # Sentry.configure_scope do |scope|
117
+ # scope.set_tags(foo: "bar")
118
+ # end
119
+ #
120
+ # Sentry.capture_message("test message") # this event will have tags { foo: "bar" }
121
+ # ```
122
+ #
97
123
  def configure_scope(&block)
98
- get_current_hub.configure_scope(&block)
99
- end
100
-
101
- def send_event(event)
102
- get_current_client.send_event(event)
103
- end
104
-
105
- def capture_event(event)
106
- get_current_hub.capture_event(event)
124
+ get_current_hub&.configure_scope(&block)
125
+ end
126
+
127
+ # Takes a block and yields a temporary scope.
128
+ # The temporary scope will inherit all the attributes from the current active scope and replace it to be the active
129
+ # scope inside the block. For example:
130
+ #
131
+ # ```ruby
132
+ # Sentry.configure_scope do |scope|
133
+ # scope.set_tags(foo: "bar")
134
+ # end
135
+ #
136
+ # Sentry.capture_message("test message") # this event will have tags { foo: "bar" }
137
+ #
138
+ # Sentry.with_scope do |temp_scope|
139
+ # temp_scope.set_tags(foo: "baz")
140
+ # Sentry.capture_message("test message 2") # this event will have tags { foo: "baz" }
141
+ # end
142
+ #
143
+ # Sentry.capture_message("test message 3") # this event will have tags { foo: "bar" }
144
+ # ```
145
+ #
146
+ def with_scope(&block)
147
+ get_current_hub&.with_scope(&block)
107
148
  end
108
149
 
150
+ # Takes an exception and reports it to Sentry via the currently active hub.
109
151
  def capture_exception(exception, **options, &block)
110
- get_current_hub.capture_exception(exception, **options, &block)
152
+ get_current_hub&.capture_exception(exception, **options, &block)
111
153
  end
112
154
 
155
+ # Takes a message string and reports it to Sentry via the currently active hub.
113
156
  def capture_message(message, **options, &block)
114
- get_current_hub.capture_message(message, **options, &block)
157
+ get_current_hub&.capture_message(message, **options, &block)
115
158
  end
116
159
 
160
+ # Takes an instance of Sentry::Event and dispatches it to the currently active hub.
161
+ def capture_event(event)
162
+ get_current_hub&.capture_event(event)
163
+ end
164
+
165
+ # Takes or initializes a new Sentry::Transaction and makes a sampling decision for it.
117
166
  def start_transaction(**options)
118
- get_current_hub.start_transaction(**options)
167
+ get_current_hub&.start_transaction(**options)
119
168
  end
120
169
 
170
+ # Returns the id of the lastly reported Sentry::Event.
121
171
  def last_event_id
122
- get_current_hub.last_event_id
172
+ get_current_hub&.last_event_id
123
173
  end
124
174
 
125
175
  def sys_command(command)
@@ -128,5 +178,13 @@ module Sentry
128
178
 
129
179
  result.strip
130
180
  end
181
+
182
+ def initialized?
183
+ !!@main_hub
184
+ end
185
+
186
+ def logger
187
+ configuration.logger
188
+ end
131
189
  end
132
190
  end
@@ -0,0 +1,37 @@
1
+ require "concurrent/executor/thread_pool_executor"
2
+ require "concurrent/executor/immediate_executor"
3
+
4
+ module Sentry
5
+ class BackgroundWorker
6
+ attr_reader :max_queue, :number_of_threads
7
+
8
+ def initialize(configuration)
9
+ @max_queue = 30
10
+ @number_of_threads = configuration.background_worker_threads
11
+
12
+ @executor =
13
+ if configuration.async
14
+ configuration.logger.debug(LOGGER_PROGNAME) { "config.async is set, BackgroundWorker is disabled" }
15
+ Concurrent::ImmediateExecutor.new
16
+ elsif @number_of_threads == 0
17
+ configuration.logger.debug(LOGGER_PROGNAME) { "config.background_worker_threads is set to 0, all events will be sent synchronously" }
18
+ Concurrent::ImmediateExecutor.new
19
+ else
20
+ configuration.logger.debug(LOGGER_PROGNAME) { "initialized a background worker with #{@number_of_threads} threads" }
21
+
22
+ Concurrent::ThreadPoolExecutor.new(
23
+ min_threads: 0,
24
+ max_threads: @number_of_threads,
25
+ max_queue: @max_queue,
26
+ fallback_policy: :discard
27
+ )
28
+ end
29
+ end
30
+
31
+ def perform(&block)
32
+ @executor.post do
33
+ block.call
34
+ end
35
+ end
36
+ end
37
+ end