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