stoplight 3.0.0 → 4.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|