gruf 2.7.0 → 2.10.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/CHANGELOG.md +32 -3
- data/README.md +7 -502
- data/gruf.gemspec +26 -8
- data/lib/gruf/cli/executor.rb +18 -6
- data/lib/gruf/client.rb +27 -2
- data/lib/gruf/client/error.rb +2 -0
- data/lib/gruf/configuration.rb +3 -2
- data/lib/gruf/controllers/base.rb +2 -0
- data/lib/gruf/controllers/request.rb +8 -6
- data/lib/gruf/controllers/service_binder.rb +1 -1
- data/lib/gruf/error.rb +1 -1
- data/lib/gruf/hooks/executor.rb +1 -1
- data/lib/gruf/interceptors/client_interceptor.rb +8 -16
- data/lib/gruf/interceptors/context.rb +4 -4
- data/lib/gruf/interceptors/instrumentation/output_metadata_timer.rb +2 -4
- data/lib/gruf/interceptors/instrumentation/request_logging/formatters/base.rb +3 -1
- data/lib/gruf/interceptors/instrumentation/request_logging/formatters/logstash.rb +3 -1
- data/lib/gruf/interceptors/instrumentation/request_logging/formatters/plain.rb +3 -1
- data/lib/gruf/interceptors/instrumentation/request_logging/interceptor.rb +13 -15
- data/lib/gruf/interceptors/instrumentation/statsd.rb +2 -4
- data/lib/gruf/interceptors/timer.rb +5 -2
- data/lib/gruf/outbound/request_context.rb +1 -1
- data/lib/gruf/server.rb +2 -2
- data/lib/gruf/timer.rb +5 -4
- data/lib/gruf/version.rb +1 -1
- metadata +226 -27
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b132efe87ccf85ffcf897a67f5c268b8696e60e5f2b280c93c52f08fea1aa122
|
4
|
+
data.tar.gz: 6a1d99886e075daac23eadeabd5e347c92d4fdc7659f7dd639d7e79e38c6b6f8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b5140ea90e6bc018cef228f7bece3096cd389bec36cc4b80f5e1a9b2ebefbab7afe494146c9f4843418f06ffe27d404885d470477c732986befe94b69191297d
|
7
|
+
data.tar.gz: fb3baf2ad10bf606b57c37f45d419e11c41bb34ce56c9801d3367cb471cfe22f097b42bbfa60aa77cbb3eda338c947eee48f909b2866df1bf660953772ac2b64
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,35 @@ Changelog for the gruf gem. This includes internal history before the gem was ma
|
|
2
2
|
|
3
3
|
### Pending release
|
4
4
|
|
5
|
+
### 2.10.0
|
6
|
+
|
7
|
+
- Drop support for Ruby 2.4/2.5 to align with Ruby EOL schedule, supporting 2.6+ only
|
8
|
+
- Allow for float/TimeSpec timeout values on clients
|
9
|
+
|
10
|
+
### 2.9.0
|
11
|
+
|
12
|
+
- Change to racially neutral terminology across library
|
13
|
+
- blacklist->blocklist
|
14
|
+
- master->main branch
|
15
|
+
- Explicitly declare development dependencies in gemspec
|
16
|
+
- Add script/e2e test for full e2e test in regression suite
|
17
|
+
- Explicitly declare [json gem](https://rubygems.org/gems/json) dependency
|
18
|
+
- Update to Rubocop 1.4, add in rubocop-rspec for spec tests
|
19
|
+
- Add Ruby 2.7, 3.0 support
|
20
|
+
|
21
|
+
### 2.8.1
|
22
|
+
|
23
|
+
- Fix issue with --suppress-default-interceptors not working [#95]
|
24
|
+
- Loosen rake development dependency to >= 10.0 [#97]
|
25
|
+
|
26
|
+
### 2.8.0
|
27
|
+
|
28
|
+
- Pass the controller request object into the request logging formatters [#92]
|
29
|
+
|
30
|
+
### 2.7.1
|
31
|
+
|
32
|
+
- Add `channel_credentials` option to `Gruf::Client` and `default_channel_credentials` option to `Gruf::Configuration` [#85] [#87]
|
33
|
+
|
5
34
|
### 2.7.0
|
6
35
|
|
7
36
|
- Add hook support for executing code paths before a server is started, and after a server stops
|
@@ -132,11 +161,11 @@ Gruf 2.0 is a major shift from Gruf 1.0. See [UPGRADING.md](UPGRADING.md) for de
|
|
132
161
|
### 1.2.4
|
133
162
|
|
134
163
|
- Loosen explicit Protobuf dependency now that 3.4.0.2 is released
|
135
|
-
- Guard against nil params in logger
|
164
|
+
- Guard against nil params in logger blocklist
|
136
165
|
|
137
166
|
### 1.2.3
|
138
167
|
|
139
|
-
- Support nested
|
168
|
+
- Support nested blocklist parameters in path.to.key format
|
140
169
|
|
141
170
|
### 1.2.2
|
142
171
|
|
@@ -158,7 +187,7 @@ Gruf 2.0 is a major shift from Gruf 1.0. See [UPGRADING.md](UPGRADING.md) for de
|
|
158
187
|
- Add configuration to set the error message when an uncaught exception is
|
159
188
|
handled by gruf.
|
160
189
|
- Add a request logging hook for Rails-style request logging, with optional
|
161
|
-
parameter logging,
|
190
|
+
parameter logging, blocklists, and formatter support
|
162
191
|
- Optimizations around Symbol casting within service calls
|
163
192
|
|
164
193
|
### 1.1.0
|
data/README.md
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
# gruf - gRPC Ruby Framework
|
2
2
|
|
3
|
-
[](https://circleci.com/gh/bigcommerce/gruf/tree/main) [](https://badge.fury.io/rb/gruf) [](https://inch-ci.org/github/bigcommerce/gruf?branch=main)
|
4
4
|
|
5
|
-
gruf is a Ruby framework that wraps the [gRPC Ruby library](https://github.com/grpc/grpc/tree/
|
5
|
+
gruf is a Ruby framework that wraps the [gRPC Ruby library](https://github.com/grpc/grpc/tree/main/src/ruby) to
|
6
6
|
provide a more streamlined integration into Ruby and Ruby on Rails applications.
|
7
7
|
|
8
8
|
It provides an abstracted server and client for gRPC services, along with other tools to help get gRPC services in Ruby
|
@@ -17,515 +17,20 @@ up fast and efficiently at scale. Some of its features include:
|
|
17
17
|
still preserving gRPC BadStatus codes
|
18
18
|
* Server and client execution timings in responses
|
19
19
|
|
20
|
-
gruf currently has active support for gRPC 1.10.x+. gruf is compatible and tested with Ruby 2.2-2.
|
20
|
+
gruf currently has active support for gRPC 1.10.x+. gruf is compatible and tested with Ruby 2.2-2.7.
|
21
21
|
gruf is also not [Rails](https://github.com/rails/rails)-specific, and can be used in any Ruby framework
|
22
|
-
(such as [Grape](https://github.com/ruby-grape/grape), for instance).
|
22
|
+
(such as [Grape](https://github.com/ruby-grape/grape) or [dry-rb](https://dry-rb.org/), for instance).
|
23
23
|
|
24
|
-
|
24
|
+
### Getting Started
|
25
25
|
|
26
|
-
|
27
|
-
|
28
|
-
```
|
29
|
-
|
30
|
-
Then in an initializer or before use:
|
31
|
-
|
32
|
-
```ruby
|
33
|
-
require 'gruf'
|
34
|
-
```
|
35
|
-
|
36
|
-
Make sure to review [UPGRADING.md](https://github.com/bigcommerce/gruf/blob/master/UPGRADING.md)
|
37
|
-
if you are upgrading gruf between minor or major versions.
|
38
|
-
|
39
|
-
### Client
|
40
|
-
|
41
|
-
Add an initializer:
|
42
|
-
|
43
|
-
```ruby
|
44
|
-
require 'gruf'
|
45
|
-
|
46
|
-
Gruf.configure do |c|
|
47
|
-
c.default_client_host = 'grpc.service.com:9003'
|
48
|
-
end
|
49
|
-
```
|
50
|
-
|
51
|
-
If you don't explicitly set `default_client_host`, you will need to pass it into the options, like so:
|
52
|
-
|
53
|
-
```ruby
|
54
|
-
client = ::Gruf::Client.new(service: ::Demo::ThingService, options: {hostname: 'grpc.service.com:9003'})
|
55
|
-
```
|
56
|
-
|
57
|
-
From there, you can instantiate a client given a stub service (say on an SslCertificates proto with a GetSslCertificate call):
|
58
|
-
|
59
|
-
```ruby
|
60
|
-
require 'gruf'
|
61
|
-
|
62
|
-
id = args[:id].to_i.presence || 1
|
63
|
-
|
64
|
-
begin
|
65
|
-
client = ::Gruf::Client.new(service: ::Demo::ThingService)
|
66
|
-
response = client.call(:GetMyThing, id: id)
|
67
|
-
puts response.message.inspect
|
68
|
-
rescue Gruf::Client::Error => e
|
69
|
-
puts e.error.inspect
|
70
|
-
end
|
71
|
-
```
|
72
|
-
|
73
|
-
Note this returns a response object. The response object can provide `trailing_metadata` as well as a `execution_time`.
|
74
|
-
|
75
|
-
### SynchronizedClient
|
76
|
-
|
77
|
-
SynchronizedClient wraps Client with some additional behavior to help prevent generating spikes
|
78
|
-
of redundant requests. If multiple calls to the same endpoint with the same parameters are made,
|
79
|
-
the first one will be executed and the following ones will block, waiting for the first result.
|
80
|
-
|
81
|
-
```ruby
|
82
|
-
require 'gruf'
|
83
|
-
require 'thwait'
|
84
|
-
|
85
|
-
id = args[:id].to_i.presence || 1
|
86
|
-
client = ::Gruf::SynchronizedClient.new(service: ::Demo::ThingService)
|
87
|
-
thread1 = Thread.new { client.call(:GetMyThing, id: id) }
|
88
|
-
thread2 = Thread.new { client.call(:GetMyThing, id: id) }
|
89
|
-
ThreadsWait.all_waits(thread1, thread2)
|
90
|
-
```
|
91
|
-
|
92
|
-
In the above example, thread1 will make the rpc call, thread2 will block until the call is complete, and then
|
93
|
-
will get the same value without making a second rpc call.
|
94
|
-
|
95
|
-
You can also skip this behavior for certain methods if desired.
|
96
|
-
|
97
|
-
```ruby
|
98
|
-
require 'gruf'
|
99
|
-
require 'thwait'
|
100
|
-
|
101
|
-
id = args[:id].to_i.presence || 1
|
102
|
-
client = ::Gruf::SynchronizedClient.new(service: ::Demo::ThingService, options: { unsynchronized_methods: [:GetMyThing] })
|
103
|
-
thread1 = Thread.new { client.call(:GetMyThing, id: id) }
|
104
|
-
thread2 = Thread.new { client.call(:GetMyThing, id: id) }
|
105
|
-
ThreadsWait.all_waits(thread1, thread2)
|
106
|
-
```
|
107
|
-
|
108
|
-
In the above example, thread1 and thread2 will make rpc calls in parallel, in the same way as if you had used
|
109
|
-
`Gruf::Client`.
|
110
|
-
|
111
|
-
### Client Interceptors
|
112
|
-
|
113
|
-
Gruf comes with an assistance class for client interceptors that you can use - or you can use the native gRPC core
|
114
|
-
interceptors. Either way, you pass them into the `client_options` when creating a client:
|
115
|
-
|
116
|
-
```ruby
|
117
|
-
class MyInterceptor < Gruf::Interceptors::ClientInterceptor
|
118
|
-
def call(request_context:)
|
119
|
-
logger.info "Got method #{request_context.method}!"
|
120
|
-
yield
|
121
|
-
end
|
122
|
-
end
|
123
|
-
|
124
|
-
::Gruf::Client.new(
|
125
|
-
service: ::Demo::ThingService,
|
126
|
-
client_options: {
|
127
|
-
interceptors: [MyInterceptor.new]
|
128
|
-
})
|
129
|
-
```
|
130
|
-
|
131
|
-
The `interceptors` option in `client_options` can accept either a `GRPC::ClientInterceptor` class or a
|
132
|
-
`Gruf::Interceptors::ClientInterceptor`, since the latter just extends the former. The gruf client interceptors
|
133
|
-
take an optional alternative approach: rather than having separate methods for each request type, it provides a default
|
134
|
-
`call` method that passes in a `RequestContext` object, which has the following attributes:
|
135
|
-
|
136
|
-
* *type* - A Symbol of the type of request (`request_response`, `server_streamer`, etc)
|
137
|
-
* *requests* An enumerable of requests being sent. For unary requests, this is a single request in an array
|
138
|
-
* *call* - The `GRPC::ActiveCall` object
|
139
|
-
* *method* - The Method being called
|
140
|
-
* *metadata* - The hash of outgoing metadata
|
141
|
-
|
142
|
-
Note that you _must_ yield back the block when building a client interceptor, so that the call can be executed.
|
143
|
-
|
144
|
-
### Server
|
145
|
-
|
146
|
-
Add an initializer:
|
147
|
-
|
148
|
-
```ruby
|
149
|
-
require 'gruf'
|
150
|
-
|
151
|
-
Gruf.configure do |c|
|
152
|
-
c.server_binding_url = 'grpc.service.com:9003'
|
153
|
-
end
|
154
|
-
```
|
155
|
-
|
156
|
-
Next, setup some handlers based on your proto configurations in `/app/rpc/`. For example, for the Thing service, with a
|
157
|
-
GetThingReq/GetThingResp call based on this proto:
|
158
|
-
|
159
|
-
```proto
|
160
|
-
syntax = "proto3";
|
161
|
-
|
162
|
-
package demo;
|
163
|
-
|
164
|
-
service Jobs {
|
165
|
-
rpc GetJob(GetJobReq) returns (GetJobResp) { }
|
166
|
-
}
|
167
|
-
|
168
|
-
message GetJobReq {
|
169
|
-
uint64 id = 1;
|
170
|
-
}
|
171
|
-
|
172
|
-
message GetJobResp {
|
173
|
-
uint64 id = 1;
|
174
|
-
string name = 2;
|
175
|
-
}
|
176
|
-
```
|
177
|
-
|
178
|
-
You'd have this handler in `/app/rpc/demo/job_controller.rb`
|
179
|
-
|
180
|
-
```ruby
|
181
|
-
module Demo
|
182
|
-
class JobController < ::Gruf::Controllers::Base
|
183
|
-
bind ::Demo::Jobs::Service
|
184
|
-
|
185
|
-
##
|
186
|
-
# @return [Demo::GetJobResp] The job response
|
187
|
-
#
|
188
|
-
def get_job
|
189
|
-
thing = Job.find(request.message.id)
|
190
|
-
|
191
|
-
Demo::GetJobResp.new(id: thing.id)
|
192
|
-
rescue
|
193
|
-
fail!(:not_found, :job_not_found, "Failed to find Job with ID: #{request.message.id}")
|
194
|
-
end
|
195
|
-
end
|
196
|
-
end
|
197
|
-
```
|
198
|
-
|
199
|
-
Finally, you can start the server by running:
|
200
|
-
|
201
|
-
```bash
|
202
|
-
bundle exec gruf
|
203
|
-
```
|
204
|
-
|
205
|
-
### Command-Line Options
|
206
|
-
|
207
|
-
Gruf comes baked in with a few command-line options for the binstub:
|
208
|
-
|
209
|
-
| Option | Description |
|
210
|
-
| ------ | ----------- |
|
211
|
-
| -h, --help | Displays the help message |
|
212
|
-
| -v, --version | Displays the gruf version |
|
213
|
-
| --host | Specify the server binding host |
|
214
|
-
| --suppress-default-interceptors | Do not use the default interceptors for the server |
|
215
|
-
| --backtrace-on-error | Push backtraces on exceptions to the error serializer |
|
216
|
-
|
217
|
-
These options will override whatever is passed in the Gruf configure block or
|
218
|
-
initializer.
|
219
|
-
|
220
|
-
### Basic Authentication
|
221
|
-
|
222
|
-
Gruf comes packaged in with a Basic Authentication interceptor. It takes in an array of supported
|
223
|
-
username and password pairs (or password-only credentials).
|
224
|
-
|
225
|
-
In Server:
|
226
|
-
|
227
|
-
```ruby
|
228
|
-
Gruf.configure do |c|
|
229
|
-
c.interceptors.use(
|
230
|
-
Gruf::Interceptors::Authentication::Basic,
|
231
|
-
credentials: [{
|
232
|
-
username: 'my-username-here',
|
233
|
-
password: 'my-password-here',
|
234
|
-
},{
|
235
|
-
username: 'another-username',
|
236
|
-
password: 'another-password',
|
237
|
-
},{
|
238
|
-
password: 'a-password-only'
|
239
|
-
}]
|
240
|
-
)
|
241
|
-
end
|
242
|
-
```
|
243
|
-
|
244
|
-
In Client:
|
245
|
-
|
246
|
-
```ruby
|
247
|
-
require 'gruf'
|
248
|
-
|
249
|
-
id = args[:id].to_i.presence || 1
|
250
|
-
|
251
|
-
options = {
|
252
|
-
username: ENV.fetch('DEMO_THING_SERVICE_USERNAME'),
|
253
|
-
password: ENV.fetch('DEMO_THING_SERVICE_PASSWORD')
|
254
|
-
}
|
255
|
-
|
256
|
-
begin
|
257
|
-
client = ::Gruf::Client.new(service: ::Demo::ThingService, options: options)
|
258
|
-
response = client.call(:GetMyThing, id: id)
|
259
|
-
puts response.message.inspect
|
260
|
-
rescue Gruf::Client::Error => e
|
261
|
-
puts e.error.inspect
|
262
|
-
end
|
263
|
-
```
|
264
|
-
|
265
|
-
Supporting an array of credentials allow for unique credentials per service, or for easy credential
|
266
|
-
rotation with zero downtime.
|
267
|
-
|
268
|
-
### SSL Configuration
|
269
|
-
|
270
|
-
We don't recommend using TLS for gRPC, but instead using something like [linkerd](https://linkerd.io) for TLS
|
271
|
-
encryption between services. If you need it, however, this library supports TLS.
|
272
|
-
|
273
|
-
For the client, you'll need to point to the public certificate:
|
274
|
-
|
275
|
-
```ruby
|
276
|
-
::Gruf::Client.new(
|
277
|
-
service: Demo::ThingService,
|
278
|
-
options: {
|
279
|
-
ssl_certificate: 'x509 public certificate here',
|
280
|
-
# OR
|
281
|
-
ssl_certificate_file: '/path/to/my.crt'
|
282
|
-
}
|
283
|
-
)
|
284
|
-
```
|
285
|
-
|
286
|
-
If you want to run a server you'll need both the CRT and the key file if you want to do credentialed auth:
|
287
|
-
|
288
|
-
```ruby
|
289
|
-
Gruf.configure do |c|
|
290
|
-
c.use_ssl = true
|
291
|
-
c.ssl_crt_file = "#{Rails.root}/config/ssl/#{Rails.env}.crt"
|
292
|
-
c.ssl_key_file = "#{Rails.root}/config/ssl/#{Rails.env}.key"
|
293
|
-
end
|
294
|
-
```
|
295
|
-
|
296
|
-
### GRPC::RpcServer configuration
|
297
|
-
To customize parameters for the underlying GRPC::RpcServer, such as the size of the gRPC thread pool,
|
298
|
-
you can pass them in via Gruf.rpc\_server\_options.
|
299
|
-
|
300
|
-
```ruby
|
301
|
-
Gruf.configure do |c|
|
302
|
-
# The size of the underlying thread pool. No more concurrent requests can be made
|
303
|
-
# than the size of the thread pool.
|
304
|
-
c.rpc_server_options[:pool_size] = 100
|
305
|
-
end
|
306
|
-
```
|
307
|
-
|
308
|
-
## Server Interceptors
|
309
|
-
|
310
|
-
gruf supports interceptors around the grpc server calls, allowing you to perform actions around your service
|
311
|
-
method calls. This can be used to add tracing data, connection resets in the grpc thread pool, further
|
312
|
-
instrumentation, and other things.
|
313
|
-
|
314
|
-
Adding a hook is as simple as creating a class that extends `Gruf::Interceptor::ServerInterceptor`,
|
315
|
-
and a `call` method that yields control to get the method result:
|
316
|
-
|
317
|
-
```ruby
|
318
|
-
class MyInterceptor < ::Gruf::Interceptors::ServerInterceptor
|
319
|
-
def call
|
320
|
-
yield
|
321
|
-
end
|
322
|
-
end
|
323
|
-
```
|
324
|
-
|
325
|
-
Interceptors have access to the `request` object, which is the `Gruf::Controller::Request` object
|
326
|
-
described above.
|
327
|
-
|
328
|
-
### Failing in an Interceptor
|
329
|
-
|
330
|
-
Interceptors can fail requests with the same method calls as a controller:
|
331
|
-
|
332
|
-
```ruby
|
333
|
-
class MyFailingInterceptor < ::Gruf::Interceptors::ServerInterceptor
|
334
|
-
def call
|
335
|
-
result = yield # this returns the protobuf message
|
336
|
-
unless result.dont_hijack
|
337
|
-
# we'll assume this "dont_hijack" attribute exists on the message for this example
|
338
|
-
fail!(:internal, :hijacked, 'Hijack all the things!')
|
339
|
-
end
|
340
|
-
result
|
341
|
-
end
|
342
|
-
end
|
343
|
-
```
|
344
|
-
|
345
|
-
Similarly, you can raise `GRPC::BadStatus` calls to trigger similar errors without accompanying metadata.
|
346
|
-
|
347
|
-
### Configuring Interceptors
|
348
|
-
|
349
|
-
From there, the interceptor can be added to the server manually (if not executing via `bundle exec gruf`):
|
350
|
-
|
351
|
-
```ruby
|
352
|
-
server = Gruf::Server.new
|
353
|
-
server.add_interceptor(MyInterceptor, option_foo: 'value 123')
|
354
|
-
```
|
355
|
-
|
356
|
-
Or, alternatively, the more common method of passing them into the `interceptors` configuration hash:
|
357
|
-
|
358
|
-
```ruby
|
359
|
-
Gruf.configure do |c|
|
360
|
-
c.interceptors.use(MyInterceptor, option_foo: 'value 123')
|
361
|
-
end
|
362
|
-
```
|
363
|
-
|
364
|
-
Interceptors each wrap the call and are run recursively within each other. This means that if you have
|
365
|
-
three interceptors - `Interceptor1`, `Interceptor2`, and `Interceptor3` - they will run in FIFO
|
366
|
-
(first in, first out) order. `Interceptor1` will run, yielding to `Interceptor2`,
|
367
|
-
which will then yield to `Interceptor3`, which will then yield to your service method call,
|
368
|
-
ending the chain.
|
369
|
-
|
370
|
-
You can utilize the `insert_before` and `insert_after` methods to maintain order:
|
371
|
-
|
372
|
-
```ruby
|
373
|
-
Gruf.configure do |c|
|
374
|
-
c.interceptors.use(Interceptor1)
|
375
|
-
c.interceptors.use(Interceptor2)
|
376
|
-
c.interceptors.insert_before(Interceptor2, Interceptor3) # 3 will now happen before 2
|
377
|
-
c.interceptors.insert_after(Interceptor1, Interceptor4) # 4 will now happen after 1
|
378
|
-
end
|
379
|
-
```
|
380
|
-
|
381
|
-
By default, the ActiveRecord Connection Reset interceptor and Output Metadata Timing interceptor
|
382
|
-
are loaded into gruf unless explicitly told not to via the `use_default_interceptors` configuration
|
383
|
-
parameter.
|
384
|
-
|
385
|
-
## Hooks
|
386
|
-
|
387
|
-
Hooks, unlike interceptors, are executed outside of the request chain, such as when a server starts
|
388
|
-
or stops. They run in FIFO order sequentially and do not wrap one another. They can be used to provide
|
389
|
-
custom boot sequences, external instrumentation support, or shutdown alerting.
|
390
|
-
|
391
|
-
You can create a hook by extending the `Gruf::Hooks::Base` class and defining the methods
|
392
|
-
on the hook you wish to implement:
|
393
|
-
|
394
|
-
```ruby
|
395
|
-
class MyHook < Gruf::Hooks::Base
|
396
|
-
def before_server_start(server:)
|
397
|
-
# do my thing before the server starts
|
398
|
-
end
|
399
|
-
|
400
|
-
def after_server_stop(server:)
|
401
|
-
# do my thing after the server stops
|
402
|
-
end
|
403
|
-
end
|
404
|
-
|
405
|
-
# Then in an initializer:
|
406
|
-
|
407
|
-
Gruf.configure do |c|
|
408
|
-
c.hooks.use(MyHook, option_foo: 'value 123')
|
409
|
-
end
|
410
|
-
```
|
411
|
-
|
412
|
-
Exceptions raised in hooks will halt the execution chain and bubble up the stack appropriately.
|
413
|
-
|
414
|
-
### Available Hook Insertion Points
|
415
|
-
|
416
|
-
Current hook insertion points are:
|
417
|
-
|
418
|
-
* `before_server_start` - Right before the gRPC server starts
|
419
|
-
* `after_server_stop` - Right after the gRPC server is shutdown
|
420
|
-
|
421
|
-
Note that exceptions raised in `before_server_start` will halt the execution chain for the remaining
|
422
|
-
`before_server_start` hooks, but will still execute the `after_server_stop` hooks as expected. Exceptions raised
|
423
|
-
in `after_server_stop` will prevent further `after_server_stop` hooks from running.
|
424
|
-
|
425
|
-
## Instrumentation
|
426
|
-
|
427
|
-
gruf comes out of the box with a couple of instrumentation interceptors packed in:
|
428
|
-
output metadata timings and StatsD support.
|
429
|
-
|
430
|
-
### Output Metadata Timing
|
431
|
-
|
432
|
-
Enabled by default, this will push timings for _successful responses_ through the response output
|
433
|
-
metadata back to the client.
|
434
|
-
|
435
|
-
### StatsD
|
436
|
-
|
437
|
-
The StatsD support is not enabled by default. To enable it, you'll want to do:
|
438
|
-
|
439
|
-
```ruby
|
440
|
-
Gruf.configure do |c|
|
441
|
-
c.interceptors.use(
|
442
|
-
Gruf::Interceptors::Instrumentation::Statsd,
|
443
|
-
client: ::Statsd.new('my.statsd.host', 8125),
|
444
|
-
prefix: 'my_application_prefix.rpc'
|
445
|
-
)
|
446
|
-
end
|
447
|
-
```
|
448
|
-
|
449
|
-
This will measure counts and timings for each endpoint.
|
450
|
-
|
451
|
-
### Request Logging
|
452
|
-
|
453
|
-
Gruf 1.2+ comes built with request logging out of the box; you'll get Rails-style logs with your
|
454
|
-
gRPC calls:
|
455
|
-
|
456
|
-
```
|
457
|
-
# plain
|
458
|
-
I, [2017-07-14T09:50:54.200506 #70571] INFO -- : [GRPC::Ok] (thing_service.get_thing) [0.348ms]
|
459
|
-
# logstash
|
460
|
-
I, [2017-07-14T09:51:03.299050 #70595] INFO -- : {"message":"[GRPC::Ok] (thing_service.get_thing) [0.372ms]","service":"thing_service","method":"thing_service.get_thing","grpc_status":"GRPC::Ok"}
|
461
|
-
```
|
462
|
-
|
463
|
-
It supports formatters (including custom ones) that you can use to specify the formatting of the logging:
|
464
|
-
|
465
|
-
```ruby
|
466
|
-
Gruf.configure do |c|
|
467
|
-
c.interceptors.use(
|
468
|
-
Gruf::Interceptors::Instrumentation::RequestLogging::Interceptor,
|
469
|
-
formatter: :logstash
|
470
|
-
)
|
471
|
-
end
|
472
|
-
```
|
473
|
-
|
474
|
-
It comes with a few more options as well:
|
475
|
-
|
476
|
-
| Option | Description | Default |
|
477
|
-
| ------ | ----------- | ------- |
|
478
|
-
| formatter | The formatter to use. By default `:plain` and `:logstash` are supported. | `:logstash` |
|
479
|
-
| log_parameters | If set to true, will log parameters in the response | `false` |
|
480
|
-
| blacklist | An array of parameter key names to redact from logging, in path.to.key format | `[]` |
|
481
|
-
| redacted_string | The string to use for redacted parameters. | `REDACTED` |
|
482
|
-
| ignore_methods | An array of method names to ignore from logging. E.g. `['namespace.health.check']` | `[]` |
|
483
|
-
|
484
|
-
It's important to maintain a safe blacklist should you decide to log parameters; gruf does no
|
485
|
-
parameter sanitization on its own. We also recommend blacklisting parameters that may contain
|
486
|
-
very large values (such as binary or json data).
|
487
|
-
|
488
|
-
## Testing with RSpec
|
489
|
-
|
490
|
-
There is a gem specifically for easy testing with RSpec: [gruf-rspec](https://github.com/bigcommerce/gruf-rspec). Take
|
491
|
-
a look at its README for more information.
|
492
|
-
|
493
|
-
## Plugins
|
494
|
-
|
495
|
-
You can build your own hooks and middleware for gruf; here's a list of known open source gems for
|
496
|
-
gruf that you can use today:
|
497
|
-
|
498
|
-
* [gruf-lightstep](https://github.com/bigcommerce/gruf-lightstep) - Provides a seamless
|
499
|
-
[LightStep](https://lightstep.com) integration
|
500
|
-
* [gruf-zipkin](https://github.com/bigcommerce/gruf-zipkin) - Provides a [Zipkin](https://zipkin.io)
|
501
|
-
integration
|
502
|
-
* [gruf-newrelic](https://github.com/bigcommerce/gruf-newrelic) - Easy [New Relic](https://newrelic.com/) integration
|
503
|
-
* [gruf-commander](https://github.com/bigcommerce/gruf-commander) - Request/command-style validation and
|
504
|
-
execution patterns for services
|
505
|
-
* [gruf-profiler](https://github.com/bigcommerce/gruf-profiler) - Profiles and provides memory usage
|
506
|
-
reports for clients and services
|
507
|
-
* [gruf-circuit-breaker](https://github.com/bigcommerce/gruf-circuit-breaker) - Circuit breaker
|
508
|
-
support for services
|
26
|
+
Please see the [gruf wiki](https://github.com/bigcommerce/gruf/wiki) for detailed information on getting started
|
27
|
+
using gruf.
|
509
28
|
|
510
29
|
## Demo Rails App
|
511
30
|
|
512
31
|
There is a [demonstration Rails application here](https://github.com/bigcommerce/gruf-demo) you can
|
513
32
|
view and clone that shows how to integrate Gruf into an existing Rails application.
|
514
33
|
|
515
|
-
## Roadmap
|
516
|
-
|
517
|
-
### Gruf 3.0
|
518
|
-
|
519
|
-
* Change configuration to an injectable object to ensure thread safety on chained server/client interactions
|
520
|
-
* Move all references to `Gruf.` configuration into injectable parameters
|
521
|
-
* Redo server configuration to be fully injectable
|
522
|
-
|
523
|
-
## Companies Using Gruf
|
524
|
-
|
525
|
-
Using gruf and want to show your support? Let us know and we'll add your name here.
|
526
|
-
|
527
|
-
* [BigCommerce](https://www.bigcommerce.com/)
|
528
|
-
|
529
34
|
## License
|
530
35
|
|
531
36
|
Copyright (c) 2017-present, BigCommerce Pty. Ltd. All rights reserved
|