Skip to content

Commit 94f52e4

Browse files
wagenetdblock
authored andcommitted
Instrument with ActiveSupport::Notifications
1 parent 483ec76 commit 94f52e4

File tree

6 files changed

+137
-33
lines changed

6 files changed

+137
-33
lines changed

.rubocop_todo.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ Metrics/BlockNesting:
1616
# Offense count: 4
1717
# Configuration parameters: CountComments.
1818
Metrics/ClassLength:
19-
Max: 246
19+
Max: 252
2020

2121
# Offense count: 23
2222
Metrics/CyclomaticComplexity:

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ Next Release
88
* [#1047](https://github.com/ruby-grape/grape/pull/1047): Adds `given` to DSL::Parameters, allowing for dependent params - [@rnubel](https://github.com/rnubel).
99
* [#1064](https://github.com/ruby-grape/grape/pull/1064): Add public `Grape::Exception::ValidationErrors#full_messages` - [@romanlehnert](https://github.com/romanlehnert).
1010
* [#1079](https://github.com/ruby-grape/grape/pull/1079): Added `stream` method to take advantage of `Rack::Chunked` [@zbelzer](https://github.com/zbelzer).
11+
* [#1086](https://github.com/ruby-grape/grape/pull/1086): Added ActiveSupport::Notifications instrumentation - [@wagenet](https://github.com/wagenet).
1112
* Your contribution here!
1213

1314
#### Fixes

README.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@
7979
- [Reloading in Rack Applications](#reloading-in-rack-applications)
8080
- [Reloading in Rails Applications](#reloading-in-rails-applications)
8181
- [Performance Monitoring](#performance-monitoring)
82+
- [Active Support Instrumentation](#active-support-instrumentation)
83+
- [Monitoring Products](#monitoring-products)
8284
- [Contributing to Grape](#contributing-to-grape)
8385
- [Hacking on Grape](#hacking-on-grape)
8486
- [License](#license)
@@ -2628,6 +2630,34 @@ See [StackOverflow #3282655](http://stackoverflow.com/questions/3282655/ruby-on-
26282630

26292631
## Performance Monitoring
26302632

2633+
### Active Support Instrumentation
2634+
2635+
Grape has built-in support for [ActiveSupport::Notifications](http://api.rubyonrails.org/classes/ActiveSupport/Notifications.html) which provides simple hook points to instrument key parts of your application.
2636+
2637+
The following are currently supported:
2638+
2639+
#### endpoint_run.grape
2640+
2641+
The main execution of an endpoint, includes filters and rendering.
2642+
2643+
* *endpoint* - The endpoint instance
2644+
2645+
#### endpoint_render.grape
2646+
2647+
The execution of the main content block of the endpoint.
2648+
2649+
* *endpoint* - The endpoint instance
2650+
2651+
#### endpoint_run_filters.grape
2652+
2653+
* *endpoint* - The endpoint instance
2654+
* *filters* - The filters being executed
2655+
* *type* - The type of filters (before, before_validation, after_validation, after)
2656+
2657+
See the [ActiveSupport::Notifications documentation](http://api.rubyonrails.org/classes/ActiveSupport/Notifications.html] for information on how to subscribe to these events.
2658+
2659+
### Monitoring Products
2660+
26312661
Grape integrates with NewRelic via the
26322662
[newrelic-grape](https://github.com/flyerhzm/newrelic-grape) gem, and
26332663
with Librato Metrics with the [grape-librato](https://github.com/seanmoon/grape-librato) gem.

lib/grape.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
require 'active_support/core_ext/hash/deep_merge'
1616
require 'active_support/core_ext/hash/except'
1717
require 'active_support/dependencies/autoload'
18+
require 'active_support/notifications'
1819
require 'multi_json'
1920
require 'multi_xml'
2021
require 'virtus'

lib/grape/endpoint.rb

Lines changed: 42 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,16 @@ def generate_api_method(method_name, &block)
4141
if instance_methods.include?(method_name.to_sym) || instance_methods.include?(method_name.to_s)
4242
fail NameError.new("method #{method_name.inspect} already exists and cannot be used as an unbound method name")
4343
end
44+
4445
define_method(method_name, &block)
4546
method = instance_method(method_name)
4647
remove_method(method_name)
47-
proc { |endpoint_instance| method.bind(endpoint_instance).call }
48+
49+
proc do |endpoint_instance|
50+
ActiveSupport::Notifications.instrument('endpoint_render.grape', endpoint: endpoint_instance) do
51+
method.bind(endpoint_instance).call
52+
end
53+
end
4854
end
4955
end
5056

@@ -210,47 +216,49 @@ def equals?(e)
210216
protected
211217

212218
def run(env)
213-
@env = env
214-
@header = {}
219+
ActiveSupport::Notifications.instrument('endpoint_run.grape', endpoint: self, env: env) do
220+
@env = env
221+
@header = {}
215222

216-
@request = Grape::Request.new(env)
217-
@params = @request.params
218-
@headers = @request.headers
223+
@request = Grape::Request.new(env)
224+
@params = @request.params
225+
@headers = @request.headers
219226

220-
cookies.read(@request)
227+
cookies.read(@request)
221228

222-
self.class.before_each.call(self) if self.class.before_each
229+
self.class.before_each.call(self) if self.class.before_each
223230

224-
run_filters befores
231+
run_filters befores, :before
225232

226-
run_filters before_validations
233+
run_filters before_validations, :before_validation
227234

228-
# Retrieve validations from this namespace and all parent namespaces.
229-
validation_errors = []
235+
# Retrieve validations from this namespace and all parent namespaces.
236+
validation_errors = []
230237

231-
# require 'pry-byebug'; binding.pry
238+
# require 'pry-byebug'; binding.pry
232239

233-
route_setting(:saved_validations).each do |validator|
234-
begin
235-
validator.validate!(params)
236-
rescue Grape::Exceptions::Validation => e
237-
validation_errors << e
240+
route_setting(:saved_validations).each do |validator|
241+
begin
242+
validator.validate!(params)
243+
rescue Grape::Exceptions::Validation => e
244+
validation_errors << e
245+
end
238246
end
239-
end
240247

241-
if validation_errors.any?
242-
fail Grape::Exceptions::ValidationErrors, errors: validation_errors, headers: header
243-
end
248+
if validation_errors.any?
249+
fail Grape::Exceptions::ValidationErrors, errors: validation_errors, headers: header
250+
end
244251

245-
run_filters after_validations
252+
run_filters after_validations, :after_validation
246253

247-
response_object = @block ? @block.call(self) : nil
248-
run_filters afters
249-
cookies.write(header)
254+
response_object = @block ? @block.call(self) : nil
255+
run_filters afters, :after
256+
cookies.write(header)
250257

251-
# The Body commonly is an Array of Strings, the application instance itself, or a File-like object.
252-
response_object = file || [body || response_object]
253-
[status, header, response_object]
258+
# The Body commonly is an Array of Strings, the application instance itself, or a File-like object.
259+
response_object = file || [body || response_object]
260+
[status, header, response_object]
261+
end
254262
end
255263

256264
def build_middleware
@@ -305,9 +313,11 @@ def helpers
305313
mod
306314
end
307315

308-
def run_filters(filters)
309-
(filters || []).each do |filter|
310-
instance_eval(&filter)
316+
def run_filters(filters, type = :other)
317+
ActiveSupport::Notifications.instrument('endpoint_run_filters.grape', endpoint: self, filters: filters, type: type) do
318+
(filters || []).each do |filter|
319+
instance_eval(&filter)
320+
end
311321
end
312322
end
313323

spec/grape/endpoint_spec.rb

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -962,4 +962,66 @@ def memoized
962962
expect(last_response.headers['Access-Control-Allow-Origin']).to eq('*')
963963
end
964964
end
965+
966+
context 'instrumentation' do
967+
before do
968+
subject.before do
969+
# Placeholder
970+
end
971+
subject.get do
972+
'hello'
973+
end
974+
975+
@events = []
976+
@subscriber = ActiveSupport::Notifications.subscribe(/grape/) do |*args|
977+
@events << ActiveSupport::Notifications::Event.new(*args)
978+
end
979+
end
980+
981+
after do
982+
ActiveSupport::Notifications.unsubscribe(@subscriber)
983+
end
984+
985+
it 'notifies AS::N' do
986+
get '/'
987+
988+
# In order that the events finalized (time each block ended)
989+
expect(@events).to contain_exactly(
990+
have_attributes(name: 'endpoint_run_filters.grape', payload: { endpoint: an_instance_of(Grape::Endpoint),
991+
filters: a_collection_containing_exactly(an_instance_of(Proc)),
992+
type: :before }),
993+
have_attributes(name: 'endpoint_run_filters.grape', payload: { endpoint: an_instance_of(Grape::Endpoint),
994+
filters: [],
995+
type: :before_validation }),
996+
have_attributes(name: 'endpoint_run_filters.grape', payload: { endpoint: an_instance_of(Grape::Endpoint),
997+
filters: [],
998+
type: :after_validation }),
999+
have_attributes(name: 'endpoint_render.grape', payload: { endpoint: an_instance_of(Grape::Endpoint) }),
1000+
have_attributes(name: 'endpoint_run_filters.grape', payload: { endpoint: an_instance_of(Grape::Endpoint),
1001+
filters: [],
1002+
type: :after }),
1003+
have_attributes(name: 'endpoint_run.grape', payload: { endpoint: an_instance_of(Grape::Endpoint),
1004+
env: an_instance_of(Hash) })
1005+
)
1006+
1007+
# In order that events were initialized
1008+
expect(@events.sort_by(&:time)).to contain_exactly(
1009+
have_attributes(name: 'endpoint_run.grape', payload: { endpoint: an_instance_of(Grape::Endpoint),
1010+
env: an_instance_of(Hash) }),
1011+
have_attributes(name: 'endpoint_run_filters.grape', payload: { endpoint: an_instance_of(Grape::Endpoint),
1012+
filters: a_collection_containing_exactly(an_instance_of(Proc)),
1013+
type: :before }),
1014+
have_attributes(name: 'endpoint_run_filters.grape', payload: { endpoint: an_instance_of(Grape::Endpoint),
1015+
filters: [],
1016+
type: :before_validation }),
1017+
have_attributes(name: 'endpoint_run_filters.grape', payload: { endpoint: an_instance_of(Grape::Endpoint),
1018+
filters: [],
1019+
type: :after_validation }),
1020+
have_attributes(name: 'endpoint_render.grape', payload: { endpoint: an_instance_of(Grape::Endpoint) }),
1021+
have_attributes(name: 'endpoint_run_filters.grape', payload: { endpoint: an_instance_of(Grape::Endpoint),
1022+
filters: [],
1023+
type: :after })
1024+
)
1025+
end
1026+
end
9651027
end

0 commit comments

Comments
 (0)