stoplight 3.0.1 → 4.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.
- checksums.yaml +4 -4
- data/README.md +174 -181
- data/lib/stoplight/builder.rb +70 -0
- data/lib/stoplight/circuit_breaker.rb +102 -0
- data/lib/stoplight/configurable.rb +95 -0
- data/lib/stoplight/configuration.rb +126 -0
- data/lib/stoplight/data_store/memory.rb +20 -5
- data/lib/stoplight/data_store/redis.rb +37 -5
- data/lib/stoplight/default.rb +2 -0
- data/lib/stoplight/error.rb +1 -0
- data/lib/stoplight/light/deprecated.rb +44 -0
- data/lib/stoplight/light/lockable.rb +45 -0
- data/lib/stoplight/light/runnable.rb +31 -13
- data/lib/stoplight/light.rb +69 -63
- data/lib/stoplight/rspec/generic_notifier.rb +42 -0
- data/lib/stoplight/rspec.rb +3 -0
- data/lib/stoplight/version.rb +1 -1
- data/lib/stoplight.rb +32 -8
- data/spec/spec_helper.rb +7 -0
- data/spec/stoplight/builder_spec.rb +165 -0
- data/spec/stoplight/circuit_breaker_spec.rb +43 -0
- data/spec/stoplight/configurable_spec.rb +25 -0
- data/spec/stoplight/data_store/memory_spec.rb +12 -149
- data/spec/stoplight/data_store/redis_spec.rb +26 -158
- data/spec/stoplight/error_spec.rb +10 -0
- data/spec/stoplight/light/lockable_spec.rb +93 -0
- data/spec/stoplight/light/runnable_spec.rb +14 -265
- data/spec/stoplight/light_spec.rb +4 -28
- data/spec/stoplight/notifier/generic_spec.rb +35 -35
- data/spec/stoplight/notifier/io_spec.rb +1 -0
- data/spec/stoplight/notifier/logger_spec.rb +3 -0
- data/spec/stoplight_spec.rb +17 -6
- data/spec/support/configurable.rb +69 -0
- data/spec/support/data_store/base/clear_failures.rb +18 -0
- data/spec/support/data_store/base/clear_state.rb +20 -0
- data/spec/support/data_store/base/get_all.rb +44 -0
- data/spec/support/data_store/base/get_failures.rb +30 -0
- data/spec/support/data_store/base/get_state.rb +7 -0
- data/spec/support/data_store/base/names.rb +29 -0
- data/spec/support/data_store/base/record_failures.rb +70 -0
- data/spec/support/data_store/base/set_state.rb +15 -0
- data/spec/support/data_store/base/with_notification_lock.rb +27 -0
- data/spec/support/data_store/base.rb +21 -0
- data/spec/support/database_cleaner.rb +26 -0
- data/spec/support/exception_helpers.rb +9 -0
- data/spec/support/light/runnable/color.rb +79 -0
- data/spec/support/light/runnable/run.rb +247 -0
- data/spec/support/light/runnable/state.rb +31 -0
- data/spec/support/light/runnable.rb +5 -0
- metadata +53 -231
- data/lib/stoplight/notifier/bugsnag.rb +0 -37
- data/lib/stoplight/notifier/honeybadger.rb +0 -44
- data/lib/stoplight/notifier/pagerduty.rb +0 -21
- data/lib/stoplight/notifier/raven.rb +0 -40
- data/lib/stoplight/notifier/rollbar.rb +0 -39
- data/lib/stoplight/notifier/slack.rb +0 -21
- data/spec/stoplight/notifier/bugsnag_spec.rb +0 -90
- data/spec/stoplight/notifier/honeybadger_spec.rb +0 -88
- data/spec/stoplight/notifier/pagerduty_spec.rb +0 -40
- data/spec/stoplight/notifier/raven_spec.rb +0 -90
- data/spec/stoplight/notifier/rollbar_spec.rb +0 -90
- data/spec/stoplight/notifier/slack_spec.rb +0 -46
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 26a32d4e3aa3c515fb23a75f3055d250eaf10431eeae1316086bfe69ca569277
|
4
|
+
data.tar.gz: 9cfadd0dc4bbc99687aeffc847ae68b9926b29383530a2d55ae79ac2b22039b6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5ee973ed322845425185904de1d681064794d3a39e7d303c36b172b377d080541867e81df5cfc2a1a75da07b1088dcf15de61c807d446ca3d60582c1d645d07f
|
7
|
+
data.tar.gz: 16420c055193f5c9364d517ecea8d4ffb1ad9fbd184a77f792d1b693ab13b5d9040f56b53027c1cfcc53b971590cb7c4ffb215c2cf3692e6c978bb8f51d592b3
|
data/README.md
CHANGED
@@ -9,7 +9,6 @@ Stoplight is traffic control for code. It's an implementation of the circuit
|
|
9
9
|
breaker pattern in Ruby.
|
10
10
|
|
11
11
|
---
|
12
|
-
|
13
12
|
Does your code use unreliable systems, like a flaky database or a spotty web
|
14
13
|
service? Wrap calls to those up in stoplights to prevent them from affecting
|
15
14
|
the rest of your application.
|
@@ -17,97 +16,88 @@ the rest of your application.
|
|
17
16
|
Check out [stoplight-admin][] for controlling your stoplights.
|
18
17
|
|
19
18
|
- [Installation](#installation)
|
20
|
-
- [Basic
|
21
|
-
- [Custom
|
22
|
-
- [Custom
|
23
|
-
- [Custom
|
24
|
-
- [Custom
|
19
|
+
- [Basic Usage](#basic-usage)
|
20
|
+
- [Custom Errors](#custom-errors)
|
21
|
+
- [Custom Fallback](#custom-fallback)
|
22
|
+
- [Custom Threshold](#custom-threshold)
|
23
|
+
- [Custom Window Size](#custom-window-size)
|
24
|
+
- [Custom Cool Off Time](#custom-cool-off-time)
|
25
25
|
- [Rails](#rails)
|
26
26
|
- [Setup](#setup)
|
27
|
-
- [Data
|
27
|
+
- [Data Store](#data-store)
|
28
28
|
- [Redis](#redis)
|
29
29
|
- [Notifiers](#notifiers)
|
30
|
-
- [
|
31
|
-
- [Honeybadger](#honeybadger)
|
30
|
+
- [IO](#io)
|
32
31
|
- [Logger](#logger)
|
33
|
-
- [
|
34
|
-
- [
|
35
|
-
- [Sentry](#sentry)
|
36
|
-
- [Slack](#slack)
|
32
|
+
- [Community-supported Notifiers](#community-supported-notifiers)
|
33
|
+
- [How to Implement Your Own Notifier?](#how-to-implement-your-own-notifier)
|
37
34
|
- [Rails](#rails-1)
|
38
|
-
- [Advanced
|
35
|
+
- [Advanced Usage](#advanced-usage)
|
39
36
|
- [Locking](#locking)
|
40
37
|
- [Testing](#testing)
|
38
|
+
- [Maintenance Policy](#maintenance-policy)
|
41
39
|
- [Credits](#credits)
|
42
40
|
|
43
41
|
## Installation
|
44
42
|
|
45
43
|
Add it to your Gemfile:
|
46
44
|
|
47
|
-
```
|
45
|
+
```ruby
|
48
46
|
gem 'stoplight'
|
49
47
|
```
|
50
48
|
|
51
49
|
Or install it manually:
|
52
50
|
|
53
|
-
```
|
51
|
+
```sh
|
54
52
|
$ gem install stoplight
|
55
53
|
```
|
56
54
|
|
57
55
|
Stoplight uses [Semantic Versioning][]. Check out [the change log][] for a
|
58
56
|
detailed list of changes.
|
59
57
|
|
60
|
-
|
61
|
-
|
62
|
-
## Basic usage
|
58
|
+
## Basic Usage
|
63
59
|
|
64
60
|
To get started, create a stoplight:
|
65
61
|
|
66
|
-
```
|
67
|
-
light = Stoplight('example-pi')
|
68
|
-
# => #<Stoplight::Light:...>
|
62
|
+
```ruby
|
63
|
+
light = Stoplight('example-pi')
|
69
64
|
```
|
70
65
|
|
71
|
-
Then you can run it and it will return the result of calling the block. This is
|
72
|
-
the green state. (The green state corresponds to the closed state for circuit
|
73
|
-
breakers.)
|
66
|
+
Then you can run it with a block of code and it will return the result of calling the block. This is
|
67
|
+
the green state. (The green state corresponds to the closed state for circuit breakers.)
|
74
68
|
|
75
|
-
```
|
76
|
-
light.run
|
69
|
+
```ruby
|
70
|
+
light.run { 22.0 / 7 }
|
77
71
|
# => 3.142857142857143
|
78
72
|
light.color
|
79
73
|
# => "green"
|
80
74
|
```
|
81
75
|
|
82
76
|
If everything goes well, you shouldn't even be able to tell that you're using a
|
83
|
-
stoplight. That's not very interesting though, so let's
|
84
|
-
stoplight:
|
85
|
-
|
86
|
-
``` rb
|
87
|
-
light = Stoplight('example-zero') { 1 / 0 }
|
88
|
-
# => #<Stoplight::Light:...>
|
89
|
-
```
|
77
|
+
stoplight. That's not very interesting though, so let's make stoplight fail.
|
90
78
|
|
91
|
-
|
79
|
+
When you run it, the error will be recorded and passed through. After
|
92
80
|
running it a few times, the stoplight will stop trying and fail fast. This is
|
93
81
|
the red state. (The red state corresponds to the open state for circuit
|
94
|
-
|
82
|
+
breakers.)
|
95
83
|
|
96
|
-
```
|
97
|
-
light
|
84
|
+
```ruby
|
85
|
+
light = Stoplight('example-zero')
|
86
|
+
# => #<Stoplight::CircuitBreaker:...>
|
87
|
+
light.run { 1 / 0 }
|
98
88
|
# ZeroDivisionError: divided by 0
|
99
|
-
light.run
|
89
|
+
light.run { 1 / 0 }
|
100
90
|
# ZeroDivisionError: divided by 0
|
101
|
-
light.run
|
91
|
+
light.run { 1 / 0 }
|
102
92
|
# Switching example-zero from green to red because ZeroDivisionError divided by 0
|
103
93
|
# ZeroDivisionError: divided by 0
|
104
|
-
light.run
|
94
|
+
light.run { 1 / 0 }
|
105
95
|
# Stoplight::Error::RedLight: example-zero
|
106
96
|
light.color
|
107
97
|
# => "red"
|
108
98
|
```
|
109
99
|
|
110
|
-
When the
|
100
|
+
When the Stoplight changes from green to red, it will notify every configured
|
111
101
|
notifier. See [the notifiers section][] to learn more about notifiers.
|
112
102
|
|
113
103
|
The stoplight will move into the yellow state after being in the red state for
|
@@ -117,7 +107,7 @@ check out [the cool off time section][] When stoplights are yellow, they will
|
|
117
107
|
try to run their code. If it fails, they'll switch back to red. If it succeeds,
|
118
108
|
they'll switch to green.
|
119
109
|
|
120
|
-
### Custom
|
110
|
+
### Custom Errors
|
121
111
|
|
122
112
|
Some errors shouldn't cause your stoplight to move into the red state. Usually
|
123
113
|
these are handled elsewhere in your stack and don't represent real failures. A
|
@@ -140,89 +130,126 @@ provide a custom block that will be called with the error and a handler
|
|
140
130
|
never ignore the error. That means a `NoMemoryError` could change the color
|
141
131
|
of your stoplights.
|
142
132
|
|
143
|
-
```
|
144
|
-
light = Stoplight('example-not-found')
|
133
|
+
```ruby
|
134
|
+
light = Stoplight('example-not-found')
|
145
135
|
.with_error_handler do |error, handle|
|
146
|
-
|
147
|
-
|
136
|
+
if error.is_a?(ActiveRecord::RecordNotFound)
|
137
|
+
raise error
|
138
|
+
else
|
139
|
+
handle.call(error)
|
140
|
+
end
|
148
141
|
end
|
149
|
-
# => #<Stoplight::
|
150
|
-
light.run
|
142
|
+
# => #<Stoplight::CircuitBreaker:...>
|
143
|
+
light.run { User.find(123) }
|
151
144
|
# ActiveRecord::RecordNotFound: Couldn't find User with ID=123
|
152
|
-
light.run
|
145
|
+
light.run { User.find(123) }
|
153
146
|
# ActiveRecord::RecordNotFound: Couldn't find User with ID=123
|
154
|
-
light.run
|
147
|
+
light.run { User.find(123) }
|
155
148
|
# ActiveRecord::RecordNotFound: Couldn't find User with ID=123
|
156
149
|
light.color
|
157
150
|
# => "green"
|
158
151
|
```
|
159
152
|
|
160
|
-
### Custom
|
153
|
+
### Custom Fallback
|
161
154
|
|
162
155
|
By default, stoplights will re-raise errors when they're green. When they're
|
163
156
|
red, they'll raise a `Stoplight::Error::RedLight` error. You can provide a
|
164
157
|
fallback that will be called in both of these cases. It will be passed the
|
165
158
|
error if the light was green.
|
166
159
|
|
167
|
-
```
|
168
|
-
light = Stoplight('example-fallback')
|
160
|
+
```ruby
|
161
|
+
light = Stoplight('example-fallback')
|
169
162
|
.with_fallback { |e| p e; 'default' }
|
170
|
-
# => #<Stoplight::
|
171
|
-
light.run
|
163
|
+
# => #<Stoplight::CircuitBreaker:..>
|
164
|
+
light.run { 1 / 0 }
|
172
165
|
# #<ZeroDivisionError: divided by 0>
|
173
166
|
# => "default"
|
174
|
-
light.run
|
167
|
+
light.run { 1 / 0 }
|
175
168
|
# #<ZeroDivisionError: divided by 0>
|
176
169
|
# => "default"
|
177
|
-
light.run
|
170
|
+
light.run { 1 / 0 }
|
178
171
|
# Switching example-fallback from green to red because ZeroDivisionError divided by 0
|
179
172
|
# #<ZeroDivisionError: divided by 0>
|
180
173
|
# => "default"
|
181
|
-
light.run
|
174
|
+
light.run { 1 / 0 }
|
182
175
|
# nil
|
183
176
|
# => "default"
|
184
177
|
```
|
185
178
|
|
186
|
-
### Custom
|
179
|
+
### Custom Threshold
|
187
180
|
|
188
181
|
Some bits of code might be allowed to fail more or less frequently than others.
|
189
182
|
You can configure this by setting a custom threshold.
|
190
183
|
|
191
|
-
```
|
192
|
-
light = Stoplight('example-threshold')
|
184
|
+
```ruby
|
185
|
+
light = Stoplight('example-threshold')
|
193
186
|
.with_threshold(1)
|
194
|
-
# => #<Stoplight::
|
195
|
-
light.run
|
187
|
+
# => #<Stoplight::CircuitBreaker:...>
|
188
|
+
light.run { fail }
|
196
189
|
# Switching example-threshold from green to red because RuntimeError
|
197
190
|
# RuntimeError:
|
198
|
-
light.run
|
191
|
+
light.run { fail }
|
199
192
|
# Stoplight::Error::RedLight: example-threshold
|
200
193
|
```
|
201
194
|
|
202
195
|
The default threshold is `3`.
|
203
196
|
|
204
|
-
### Custom
|
197
|
+
### Custom Window Size
|
198
|
+
|
199
|
+
By default, all recorded failures, regardless of the time these happen, will count to reach
|
200
|
+
the threshold (hence turning the light to red). If needed, a window size can be set,
|
201
|
+
meaning you can control how many errors per period of time will count to reach the red
|
202
|
+
state.
|
203
|
+
|
204
|
+
By default, every recorded failure contributes to reaching the threshold, regardless of when it occurs,
|
205
|
+
causing the stoplight to turn red. By configuring a custom window size, you control how errors are
|
206
|
+
counted within a specified time frame. Here's how it works:
|
207
|
+
|
208
|
+
Let's say you set the window size to 2 seconds:
|
209
|
+
|
210
|
+
```ruby
|
211
|
+
window_size_in_seconds = 2
|
212
|
+
|
213
|
+
light = Stoplight('example-threshold')
|
214
|
+
.with_window_size(window_size_in_seconds)
|
215
|
+
.with_threshold(1) #=> #<Stoplight::CircuitBreaker:...>
|
216
|
+
|
217
|
+
light.run { 1 / 0 } #=> #<ZeroDivisionError: divided by 0>
|
218
|
+
sleep(3)
|
219
|
+
light.run { 1 / 0 }
|
220
|
+
```
|
221
|
+
|
222
|
+
Without the window size configuration, the second `light.run { 1 / 0 }` call will result in a
|
223
|
+
`Stoplight::Error::RedLight` exception being raised, as the stoplight transitions to the red state
|
224
|
+
after the first call. With a sliding window of 2 seconds, only the errors that occur within the latest
|
225
|
+
2 seconds are considered. The first error causes the stoplight to turn red, but after 3 seconds
|
226
|
+
(when the second error occurs), the window has shifted, and the stoplight switches to green state
|
227
|
+
causing the error to raise again. This provides a way to focus on the most recent errors.
|
228
|
+
|
229
|
+
The default window size is infinity, so all failures counts.
|
230
|
+
|
231
|
+
### Custom Cool Off Time
|
205
232
|
|
206
233
|
Stoplights will automatically attempt to recover after a certain amount of
|
207
234
|
time. A light in the red state for longer than the cool off period will
|
208
235
|
transition to the yellow state. This cool off time is customizable.
|
209
236
|
|
210
|
-
```
|
211
|
-
light = Stoplight('example-cool-off')
|
237
|
+
```ruby
|
238
|
+
light = Stoplight('example-cool-off')
|
212
239
|
.with_cool_off_time(1)
|
213
|
-
# => #<Stoplight::
|
214
|
-
light.run
|
240
|
+
# => #<Stoplight::CircuitBreaker:...>
|
241
|
+
light.run { fail }
|
215
242
|
# RuntimeError:
|
216
|
-
light.run
|
243
|
+
light.run { fail }
|
217
244
|
# RuntimeError:
|
218
|
-
light.run
|
245
|
+
light.run { fail }
|
219
246
|
# Switching example-cool-off from green to red because RuntimeError
|
220
247
|
# RuntimeError:
|
221
248
|
sleep(1)
|
222
249
|
# => 1
|
223
250
|
light.color
|
224
251
|
# => "yellow"
|
225
|
-
light.run
|
252
|
+
light.run { fail }
|
226
253
|
# RuntimeError:
|
227
254
|
```
|
228
255
|
|
@@ -236,19 +263,19 @@ effectively replaces the red state with yellow.
|
|
236
263
|
Stoplight was designed to wrap Rails actions with minimal effort. Here's an
|
237
264
|
example configuration:
|
238
265
|
|
239
|
-
```
|
266
|
+
```ruby
|
240
267
|
class ApplicationController < ActionController::Base
|
241
268
|
around_action :stoplight
|
242
269
|
|
243
270
|
private
|
244
271
|
|
245
272
|
def stoplight(&block)
|
246
|
-
Stoplight("#{params[:controller]}##{params[:action]}"
|
273
|
+
Stoplight("#{params[:controller]}##{params[:action]}")
|
247
274
|
.with_fallback do |error|
|
248
275
|
Rails.logger.error(error)
|
249
276
|
render(nothing: true, status: :service_unavailable)
|
250
277
|
end
|
251
|
-
.run
|
278
|
+
.run(&block)
|
252
279
|
end
|
253
280
|
end
|
254
281
|
```
|
@@ -259,10 +286,10 @@ end
|
|
259
286
|
|
260
287
|
Stoplight uses an in-memory data store out of the box.
|
261
288
|
|
262
|
-
```
|
289
|
+
```ruby
|
263
290
|
require 'stoplight'
|
264
291
|
# => true
|
265
|
-
Stoplight
|
292
|
+
Stoplight.default_data_store
|
266
293
|
# => #<Stoplight::DataStore::Memory:...>
|
267
294
|
```
|
268
295
|
|
@@ -271,17 +298,16 @@ the only supported persistent data store is Redis.
|
|
271
298
|
|
272
299
|
#### Redis
|
273
300
|
|
274
|
-
Make sure you have [the Redis gem][]
|
275
|
-
Stoplight.
|
301
|
+
Make sure you have [the Redis gem][] installed before configuring Stoplight.
|
276
302
|
|
277
|
-
```
|
303
|
+
```ruby
|
278
304
|
require 'redis'
|
279
305
|
# => true
|
280
306
|
redis = Redis.new
|
281
307
|
# => #<Redis client ...>
|
282
308
|
data_store = Stoplight::DataStore::Redis.new(redis)
|
283
309
|
# => #<Stoplight::DataStore::Redis:...>
|
284
|
-
Stoplight
|
310
|
+
Stoplight.default_data_store = data_store
|
285
311
|
# => #<Stoplight::DataStore::Redis:...>
|
286
312
|
```
|
287
313
|
|
@@ -290,38 +316,26 @@ Stoplight::Light.default_data_store = data_store
|
|
290
316
|
Stoplight sends notifications to standard error by default.
|
291
317
|
|
292
318
|
``` rb
|
293
|
-
Stoplight
|
319
|
+
Stoplight.default_notifiers
|
294
320
|
# => [#<Stoplight::Notifier::IO:...>]
|
295
321
|
```
|
296
322
|
|
297
323
|
If you want to send notifications elsewhere, you'll have to set them up.
|
298
324
|
|
299
|
-
####
|
300
|
-
|
301
|
-
Make sure you have [the Bugsnag gem][] (`~> 4.0`) installed before configuring
|
302
|
-
Stoplight.
|
303
|
-
|
304
|
-
``` rb
|
305
|
-
require 'bugsnag'
|
306
|
-
# => true
|
307
|
-
notifier = Stoplight::Notifier::Bugsnag.new(Bugsnag)
|
308
|
-
# => #<Stoplight::Notifier::Bugsnag:...>
|
309
|
-
Stoplight::Light.default_notifiers += [notifier]
|
310
|
-
# => [#<Stoplight::Notifier::IO:...>, #<Stoplight::Notifier::Bugsnag:...>]
|
311
|
-
```
|
325
|
+
#### IO
|
312
326
|
|
313
|
-
|
327
|
+
Stoplight can notify not only into STDOUT, but into any IO object. You can configure
|
328
|
+
the `Stoplight::Notifier::IO` notifier for that.
|
314
329
|
|
315
|
-
|
316
|
-
|
330
|
+
```ruby
|
331
|
+
require 'stringio'
|
317
332
|
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
Stoplight::
|
324
|
-
# => [#<Stoplight::Notifier::IO:...>, #<Stoplight::Notifier::Honeybadger:...>]
|
333
|
+
io = StringIO.new
|
334
|
+
# => #<StringIO:...>
|
335
|
+
notifier = Stoplight::Notifier::IO.new(io)
|
336
|
+
# => #<Stoplight::Notifier::IO:...>
|
337
|
+
Stoplight.default_notifiers += [notifier]
|
338
|
+
# => [#<Stoplight::Notifier::IO:...>, #<Stoplight::Notifier::IO:...>]
|
325
339
|
```
|
326
340
|
|
327
341
|
#### Logger
|
@@ -329,77 +343,48 @@ Stoplight::Light.default_notifiers += [notifier]
|
|
329
343
|
Stoplight can be configured to use [the Logger class][] from the standard
|
330
344
|
library.
|
331
345
|
|
332
|
-
```
|
346
|
+
```ruby
|
333
347
|
require 'logger'
|
334
348
|
# => true
|
335
349
|
logger = Logger.new(STDERR)
|
336
350
|
# => #<Logger:...>
|
337
351
|
notifier = Stoplight::Notifier::Logger.new(logger)
|
338
352
|
# => #<Stoplight::Notifier::Logger:...>
|
339
|
-
Stoplight
|
353
|
+
Stoplight.default_notifiers += [notifier]
|
340
354
|
# => [#<Stoplight::Notifier::IO:...>, #<Stoplight::Notifier::Logger:...>]
|
341
355
|
```
|
342
356
|
|
343
|
-
####
|
357
|
+
#### Community-supported Notifiers
|
344
358
|
|
345
|
-
|
346
|
-
Stoplight.
|
359
|
+
* [stoplight-sentry]
|
347
360
|
|
348
|
-
|
349
|
-
require 'pagerduty'
|
350
|
-
# => true
|
351
|
-
pagerduty = Pagerduty.new('http://www.example.com/webhook-url')
|
352
|
-
# => #<Pagerduty:...>
|
353
|
-
notifier = Stoplight::Notifier::Pagerduty.new(pagerduty)
|
354
|
-
# => #<Stoplight::Notifier::Pagerduty:...>
|
355
|
-
Stoplight::Light.default_notifiers += [notifier]
|
356
|
-
# => [#<Stoplight::Notifier::IO:...>, #<Stoplight::Notifier::Pagerduty:...>]
|
357
|
-
```
|
361
|
+
You you want to implement your own notifier, the following section contains all the required information.
|
358
362
|
|
359
|
-
|
363
|
+
Pull requests to update this section are welcome.
|
360
364
|
|
361
|
-
|
362
|
-
Stoplight.
|
365
|
+
#### How to implement your own notifier?
|
363
366
|
|
364
|
-
|
365
|
-
require 'rollbar'
|
366
|
-
# => true
|
367
|
-
notifier = Stoplight::Notifier::Rollbar.new(Rollbar)
|
368
|
-
# => #<Stoplight::Notifier::Rollbar:...>
|
369
|
-
Stoplight::Light.default_notifiers += [notifier]
|
370
|
-
# => [#<Stoplight::Notifier::IO:...>, #<Stoplight::Notifier::Rollbar:...>]
|
371
|
-
```
|
372
|
-
|
373
|
-
#### Sentry
|
374
|
-
|
375
|
-
Make sure you have [the Sentry gem][] (`~> 1.2`) installed before configuring
|
376
|
-
Stoplight.
|
367
|
+
A notifier has to implement the `Stoplight::Notifier::Base` interface:
|
377
368
|
|
378
|
-
```
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
# => #<Raven::Configuration:...>
|
383
|
-
notifier = Stoplight::Notifier::Raven.new(configuration)
|
384
|
-
# => #<Stoplight::Notifier::Raven:...>
|
385
|
-
Stoplight::Light.default_notifiers += [notifier]
|
386
|
-
# => [#<Stoplight::Notifier::IO:...>, #<Stoplight::Notifier::Raven:...>]
|
369
|
+
```ruby
|
370
|
+
def notify(light, from_color, to_color, error)
|
371
|
+
raise NotImplementedError
|
372
|
+
end
|
387
373
|
```
|
388
374
|
|
389
|
-
|
375
|
+
For convenience, you can use the `Stoplight::Notifier::Generic` module. It takes care of
|
376
|
+
the message formatting, and you have to implement only the `put` method, which takes message sting as an argument:
|
390
377
|
|
391
|
-
|
392
|
-
Stoplight
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
Stoplight::Light.default_notifiers += [notifier]
|
402
|
-
# => [#<Stoplight::Notifier::IO:...>, #<Stoplight::Notifier::Slack:...>]
|
378
|
+
```ruby
|
379
|
+
class IO < Stoplight::Notifier::Base
|
380
|
+
include Generic
|
381
|
+
|
382
|
+
private
|
383
|
+
|
384
|
+
def put(message)
|
385
|
+
@object.puts(message)
|
386
|
+
end
|
387
|
+
end
|
403
388
|
```
|
404
389
|
|
405
390
|
### Rails
|
@@ -409,11 +394,11 @@ in-memory data store, you don't need to do anything special. If you want to use
|
|
409
394
|
a persistent data store, you'll need to configure it. Create an initializer for
|
410
395
|
Stoplight:
|
411
396
|
|
412
|
-
```
|
397
|
+
```ruby
|
413
398
|
# config/initializers/stoplight.rb
|
414
399
|
require 'stoplight'
|
415
|
-
Stoplight
|
416
|
-
Stoplight
|
400
|
+
Stoplight.default_data_store = Stoplight::DataStore::Redis.new(...)
|
401
|
+
Stoplight.default_notifiers += [Stoplight::Notifier::Logger.new(Rails.logger)]
|
417
402
|
```
|
418
403
|
|
419
404
|
## Advanced usage
|
@@ -421,17 +406,17 @@ Stoplight::Light.default_notifiers += [Stoplight::Notifier::Logger.new(Rails.log
|
|
421
406
|
### Locking
|
422
407
|
|
423
408
|
Although stoplights can operate on their own, occasionally you may want to
|
424
|
-
override the default behavior. You can lock a light
|
425
|
-
|
409
|
+
override the default behavior. You can lock a light using `#lock(color)` method.
|
410
|
+
Color should be either `Stoplight::Color::GREEN` or ``Stoplight::Color::RED``.
|
426
411
|
|
427
|
-
```
|
428
|
-
light = Stoplight('example-locked')
|
429
|
-
# => #<Stoplight::
|
430
|
-
light.run
|
412
|
+
```ruby
|
413
|
+
light = Stoplight('example-locked')
|
414
|
+
# => #<Stoplight::CircuitBreaker:..>
|
415
|
+
light.run { true }
|
431
416
|
# => true
|
432
|
-
light.
|
433
|
-
# =>
|
434
|
-
light.run
|
417
|
+
light.lock(Stoplight::Color::RED)
|
418
|
+
# => #<Stoplight::CircuitBreaker:..>
|
419
|
+
light.run { true }
|
435
420
|
# Stoplight::Error::RedLight: example-locked
|
436
421
|
```
|
437
422
|
|
@@ -440,10 +425,11 @@ have configured a custom data store and that data store fails, Stoplight will
|
|
440
425
|
switch over to using a blank in-memory data store. That means you will lose the
|
441
426
|
locked state of any stoplights.
|
442
427
|
|
443
|
-
You can go back to using the default behavior by unlocking the stoplight
|
428
|
+
You can go back to using the default behavior by unlocking the stoplight using `#unlock`.
|
444
429
|
|
445
|
-
```
|
446
|
-
light.
|
430
|
+
```ruby
|
431
|
+
light.unlock
|
432
|
+
# => #<Stoplight::CircuitBreaker:..>
|
447
433
|
```
|
448
434
|
|
449
435
|
### Testing
|
@@ -453,28 +439,34 @@ However there are a few things you can do to make them behave better. If your
|
|
453
439
|
stoplights are spewing messages into your test output, you can silence them
|
454
440
|
with a couple configuration changes.
|
455
441
|
|
456
|
-
```
|
457
|
-
Stoplight
|
458
|
-
Stoplight
|
442
|
+
```ruby
|
443
|
+
Stoplight.default_error_notifier = -> _ {}
|
444
|
+
Stoplight.default_notifiers = []
|
459
445
|
```
|
460
446
|
|
461
447
|
If your tests mysteriously fail because stoplights are the wrong color, you can
|
462
448
|
try resetting the data store before each test case. For example, this would
|
463
449
|
give each test case a fresh data store with RSpec.
|
464
450
|
|
465
|
-
```
|
451
|
+
```ruby
|
466
452
|
before(:each) do
|
467
|
-
Stoplight
|
453
|
+
Stoplight.default_data_store = Stoplight::DataStore::Memory.new
|
468
454
|
end
|
469
455
|
```
|
470
456
|
|
471
457
|
Sometimes you may want to test stoplights directly. You can avoid resetting the
|
472
458
|
data store by giving each stoplight a unique name.
|
473
459
|
|
474
|
-
```
|
475
|
-
stoplight = Stoplight("test-#{rand}")
|
460
|
+
```ruby
|
461
|
+
stoplight = Stoplight("test-#{rand}")
|
476
462
|
```
|
477
463
|
|
464
|
+
## Maintenance Policy
|
465
|
+
|
466
|
+
Stoplight supports the latest three minor versions of Ruby, which currently are: `3.0.x`, `3.1.x`, and `3.2.x`. Changing
|
467
|
+
the minimum supported Ruby version is not considered a breaking change.
|
468
|
+
We support the current stable Redis version (`7.2`) and the latest release of the previous major version (`6.2.9`)
|
469
|
+
|
478
470
|
## Credits
|
479
471
|
|
480
472
|
Stoplight is brought to you by [@camdez][] and [@tfausak][] from [@OrgSync][]. [@bolshakov][] is the current
|
@@ -512,3 +504,4 @@ Stoplight is licensed under [the MIT License][].
|
|
512
504
|
[complete list of contributors]: https://github.com/bolshakov/stoplight/graphs/contributors
|
513
505
|
[CircuitBreaker]: http://martinfowler.com/bliki/CircuitBreaker.html
|
514
506
|
[the MIT license]: LICENSE.md
|
507
|
+
[stoplight-sentry]: https://github.com/bolshakov/stoplight-sentry
|